Re: Style for docstring
I don't use docstrings much; instead I put a line or two of comments after the `def ` line. But my practice in such situations is as per the OP's 3rd suggestion, e.g. # Returns True if . I'm curious as to why so many people prefer "Return" to "Returns". Checking out help() on a few functions in the stdlib, they all used "Return" or a grammatical equivalent, so this does seem to be a Python cultural thing. But why? To me, "Returns" begins a description as to what the function does, whereas "Return" is an imperative. But who is it addresed to? Is a function considered to be a sentient entity that can respond to a command? Is it an invocation to the lines of code following the docstring: "Do this!" Might not the programmer mistakenly think (if only for a moment) that the imperative is addressed to him? Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Style for docstring
Well, de gustibus non est disputandum. For me, the switch from the imperative mode to the descriptive mode produces a mild cognitive dissonance. Best wishes Rob Cliffe On 25/04/2022 23:34, Cameron Simpson wrote: On 23Apr2022 03:26, Avi Gross wrote: We know some people using "professional" language make things shorteror talk from a point of view different than others and often in otherwise incomprehensible jargon. If a programmer is taking about the algorithm that a function implements, then, yes, they may write "scan" and "return". But if they realize the darn documentation is for PEOPLE asking how to use the darn thing, and want to write in more informal and understandable English, I think it makes more sense to say what the function does as in "scans" and importantly what it "returns" to the user as a result. I'm in the imperative camp. But if I think the function requires some elaboration, _then_ I provide description: def f(x): ''' Return the frobnangle of `x`. This iterates over the internals of `x` in blah order gathering the earliest items which are frobby and composes a nangle of the items. ''' I very much like the concise imperative opening sentence, sometimes 2 sentences. Then the elaboration if the function isn't trivially obvious. Cheers, Cameron Simpson -- https://mail.python.org/mailman/listinfo/python-list
Re: Printing Unicode strings in a list
On 28/04/2022 14:27, Stephen Tucker wrote: To Cameron Simpson, Thanks for your in-depth and helpful reply. I have noted it and will be giving it close attention when I can. The main reason why I am still using Python 2.x is that my colleagues are still using a GIS system that has a Python programmer's interface - and that interface uses Python 2.x. The team are moving to an updated version of the system whose Python interface is Python 3.x. However, I am expecting to retire over the next 8 months or so, so I do not need to be concerned with Python 3.x - my successor will be doing that. Still, if you're feeling noble, you could start the work of making your code Python 3 compatible.😁 Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Non-deterministic set ordering
I was shocked to discover that when repeatedly running the following
program (condensed from a "real" program) under Python 3.8.3
for p in { ('x','y'), ('y','x') }:
print(p)
the output was sometimes
('y', 'x')
('x', 'y')
and sometimes
('x', 'y')
('y', 'x')
Can anyone explain why running identical code should result in
traversing a set in a different order?
Thanks
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Non-deterministic set ordering
On 16/05/2022 04:13, Dan Stromberg wrote:
On Sun, May 15, 2022 at 8:01 PM Rob Cliffe via Python-list
wrote:
I was shocked to discover that when repeatedly running the following
program (condensed from a "real" program) under Python 3.8.3
for p in { ('x','y'), ('y','x') }:
print(p)
the output was sometimes
('y', 'x')
('x', 'y')
and sometimes
('x', 'y')
('y', 'x')
Can anyone explain why running identical code should result in
traversing a set in a different order?
Sets are defined as unordered so that they can be hashed internally to
give O(1) operations for many tasks.
It wouldn't be unreasonable for sets to use a fixed-by-arbitrary
ordering for a given group of set operations, but being unpredictable
deters developers from mistakenly assuming they are ordered.
If you need order, you should use a tuple, list, or something like
https://grantjenks.com/docs/sortedcontainers/sortedset.html
Thanks, I can work round this behaviour.
But I'm curious: where does the variability come from? Is it deliberate
(as your answer seems to imply)? AFAIK the same code within the *same
run* of a program does produce identical results.
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Non-deterministic set ordering
Thanks, Paul. Question answered!
Rob Cliffe
On 16/05/2022 04:36, Paul Bryan wrote:
This may explain it:
https://stackoverflow.com/questions/27522626/hash-function-in-python-3-3-returns-different-results-between-sessions
On Mon, 2022-05-16 at 04:20 +0100, Rob Cliffe via Python-list wrote:
On 16/05/2022 04:13, Dan Stromberg wrote:
On Sun, May 15, 2022 at 8:01 PM Rob Cliffe via Python-list
wrote:
I was shocked to discover that when repeatedly running the following
program (condensed from a "real" program) under Python 3.8.3
for p in { ('x','y'), ('y','x') }:
print(p)
the output was sometimes
('y', 'x')
('x', 'y')
and sometimes
('x', 'y')
('y', 'x')
Can anyone explain why running identical code should result in
traversing a set in a different order?
Sets are defined as unordered so that they can be hashed internally to
give O(1) operations for many tasks.
It wouldn't be unreasonable for sets to use a fixed-by-arbitrary
ordering for a given group of set operations, but being unpredictable
deters developers from mistakenly assuming they are ordered.
If you need order, you should use a tuple, list, or something like
https://grantjenks.com/docs/sortedcontainers/sortedset.html
Thanks, I can work round this behaviour.
But I'm curious: where does the variability come from? Is it deliberate
(as your answer seems to imply)? AFAIK the same code within the *same
run* of a program does produce identical results.
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
REPL with multiple function definitions
This 2-line program def f(): pass def g(): pass runs silently (no Exception). But: 23:07:02 c:\>python Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:19) [MSC v.1925 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> def f(): pass ... def g(): pass File "", line 2 def g(): pass ^ SyntaxError: invalid syntax >>> Is there a good reason for this? Thanks Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: REPL with multiple function definitions
On 26/06/2022 23:22, Jon Ribbens via Python-list wrote: On 2022-06-26, Rob Cliffe wrote: This 2-line program def f(): pass def g(): pass runs silently (no Exception). But: 23:07:02 c:\>python Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:19) [MSC v.1925 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. def f(): pass ... def g(): pass File "", line 2 def g(): pass ^ SyntaxError: invalid syntax Is there a good reason for this? For some reason, the REPL can't cope with one-line blocks like that. If you put a blank line after each one-block line then it will work. It's actually not to do with 1-line blocks, just attempting to define 2 functions "at once": 22:27:23 C:\>python Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:19) [MSC v.1925 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> def f(): ... return 42 ... def g(): File "", line 3 def g(): ^ SyntaxError: invalid syntax >>> But you are right that adding a blank line after the first function definition solves the "problem". Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Byte arrays and DLLs
I have an application in which I wanted a fixed-length "array of bytes" (that's intended as an informal term) where I could read and write individual bytes and slices, and also pass the array to a DLL (one I wrote in C) which expects an "unsigned char *" parameter. I am using ctypes to talk to the DLL but am open to alternatives. Speed is important. My OS is Windows 10. I started off using a bytearray object (bytes does not support item assignment), but I couldn't find any way of passing it to the DLL directly. Instead I had to convert it to a different type before passing it, e.g. bytes(MyArray) or (ctypes.c_char * LEN).from_buffer(MyArray)) # LEN is the length of MyArray, knownin advance but this was slow, I think because the array data is being copied to a separate object. Eventually after consulting Googol I came up with using a memoryview: MyArray = memoryview(bytearray( )) # can read and write to this and passing it to the DLL as MyArray.tobytes() and was gratified to see a modest speed improvement. (I don't know for sure if it is still copying the array data, though I would guess not.) Is this a sensible approach, or am I still missing something? AKAIK it is not possible to give ctypes a bytearray object and persuade it to give you a pointer to the actual array data, suitable for passing to a DLL. Is this (a) false (b) for historical reasons (c) for some other good reason? TIA Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Byte arrays and DLLs
That worked. Many thanks Eryk. Rob On 30/06/2022 23:45, Eryk Sun wrote: On 6/30/22, Rob Cliffe via Python-list wrote: AKAIK it is not possible to give ctypes a bytearray object and persuade it to give you a pointer to the actual array data, suitable for passing to a DLL. You're overlooking the from_buffer() method. For example: >>> ba = bytearray(10) >>> ca = (ctypes.c_char * len(ba)).from_buffer(ba) >>> ca.value = b'spam&eggs' >>> ba bytearray(b'spam&eggs\x00') Note that the bytearray can't be resized while a view of the data is exported. For example: >>> ba.append(97) Traceback (most recent call last): File "", line 1, in BufferError: Existing exports of data: object cannot be re-sized >>> del ba[-1] Traceback (most recent call last): File "", line 1, in BufferError: Existing exports of data: object cannot be re-sized -- https://mail.python.org/mailman/listinfo/python-list
Re: for -- else: what was the motivation?
I too have occasionally used for ... else. It does have its uses. But
oh, how I wish it had been called something else more meaningful,
whether 'nobreak' or whatever. It used to really confuse me. Now I've
learned to mentally replace "else" by "if nobreak", it confuses me a bit
less.
Rob Cliffe
On 12/10/2022 22:11, Weatherby,Gerard wrote:
As did I.
tree = ET.parse(lfile)
for child in tree.getroot():
if child.tag == 'server':
break
else:
raise ValueError(f"server tag not found in {lfile}")
I think there are other places I could be using it, but honestly I tend to
forget it’s available.
From: Python-list on behalf of
Stefan Ram
Date: Wednesday, October 12, 2022 at 2:22 PM
To: [email protected]
Subject: Re: for -- else: what was the motivation?
*** Attention: This is an external email. Use caution responding, opening
attachments or clicking on links. ***
Axy writes:
So, seriously, why they needed else if the following pieces produce same
result? Does anyone know or remember their motivation?
Just wrote code wherein I used "else"! This:
import locale
for name in( 'de', 'de_DE', 'deu_deu', 'deu', 'German', 'Deutsch' ):
try: locale.setlocale( locale.LC_ALL, name ); break
except locale.Error: pass
else: print( "Programm kann deutsche Schreibweise nicht einrichten." )
.
--
https://urldefense.com/v3/__https://mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!iyDac-XjNlj78G0XwNzZ-FEHyuCZIy33n3cI9MUDM_FnEdR04mSQ5Ln0OA1ETUNloyH24iY9meNHVdixLgWRYL8$
--
https://mail.python.org/mailman/listinfo/python-list
Re: Single line if statement with a continue
On 15/12/2022 04:35, Chris Angelico wrote: On Thu, 15 Dec 2022 at 14:41, Aaron P wrote: I occasionally run across something like: for idx, thing in enumerate(things): if idx == 103: continue do_something_with(thing) It seems more succinct and cleaner to use: if idx == 103: continue. Nothing at all wrong with writing that on a single line. If you have issues with Flake8 not accepting your choices, reconfigure Flake8 :) ChrisA I'm so glad that Chris and others say this. It (i.e. if plus break/continue/return on a single line) is something I have quite often done in my own code, albeit with a feeling of guilt that I was breaking a Python taboo. Now I will do it with a clear conscience. 😁 Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Top level of a recursive function
On 14/12/2022 13:49, Stefan Ram wrote:
I also found an example similar to what was discussed here
in pypy's library file "...\Lib\_tkinter\__init__.py":
|def _flatten(item):
|def _flatten1(output, item, depth):
|if depth > 1000:
|raise ValueError("nesting too deep in _flatten")
|if not isinstance(item, (list, tuple)):
|raise TypeError("argument must be sequence")
|# copy items to output tuple
|for o in item:
|if isinstance(o, (list, tuple)):
|_flatten1(output, o, depth + 1)
|elif o is not None:
|output.append(o)
|
|result = []
|_flatten1(result, item, 0)
|return tuple(result)
.
This presumably results in an (avoidable) run-time overhead from
constructing _flatten1 every time _flatten is called (and having it
garbage-collected later).
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: ok, I feel stupid, but there must be a better way than this! (finding name of unique key in dict)
On 20/01/2023 15:29, Dino wrote:
let's say I have this list of nested dicts:
[
{ "some_key": {'a':1, 'b':2}},
{ "some_other_key": {'a':3, 'b':4}}
]
I need to turn this into:
[
{ "value": "some_key", 'a':1, 'b':2},
{ "value": "some_other_key", 'a':3, 'b':4}
]
Assuming that I believe the above, rather than the code below, this works:
listOfDescriptors = [
{ ** (L := list(D.items())[0])[1], **{'value' : L[0] } }
for D in origListOfDescriptors]
I believe that from Python 3.9 onwards this can be written more
concisely as:
listOfDescriptors = [
{ (L := list(D.items())[0])[1] } | {'value' : L[0] }
for D in origListOfDescriptors] # untested
Best wishes
Rob Cliffe
I actually did it with:
listOfDescriptors = list()
for cd in origListOfDescriptors:
cn = list(cd.keys())[0] # There must be a better way than this!
listOfDescriptors.append({
"value": cn,
"type": cd[cn]["a"],
"description": cd[cn]["b"]
})
and it works, but I look at this and think that there must be a better
way. Am I missing something obvious?
PS: Screw OpenAPI!
Dino
--
https://mail.python.org/mailman/listinfo/python-list
Re: Evaluation of variable as f-string
On 25/01/2023 19:38, Thomas Passin wrote:
Stack overflow to the rescue:
Search phrase: "python evaluate string as fstring"
https://stackoverflow.com/questions/47339121/how-do-i-convert-a-string-into-an-f-string
def effify(non_f_str: str):
return eval(f'f"""{non_f_str}"""')
print(effify(s)) # prints as expected: "-> z"
Duh! Am I the only one who feels stupid not thinking of this?
Although of course it won't work if the string already contains triple
double quotes.
I believe this could be got round with some ingenuity (having the effify
function parse the string and replace genuine (not embedded in
single-quotes) triple double quotes with triple single quotes, though
there are some complications).
And the effify function can't be put in its own module unless it can be
passed the globals and/or locals dictionaries as needed for eval to
use. Something like this:
def effify(non_f_str, glob=None, loc=None):
return eval(f'f"""{non_f_str}"""',
glob if glob is not None else globals(),
loc if loc is not None else locals())
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: bool and int
On 24/01/2023 04:22, Dino wrote: $ python Python 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> b = True >>> isinstance(b,bool) True >>> isinstance(b,int) True >>> That immediately tells you that either bool is a subclass of int int is a subclass of bool bool and int are both subclasses of some other class In fact the first one is true. This is not a logical necessity, but the way Python happens to be designed. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Evaluation of variable as f-string
On 23/01/2023 18:02, Chris Angelico wrote: On Tue, 24 Jan 2023 at 04:56, Johannes Bauer wrote: Hi there, is there an easy way to evaluate a string stored in a variable as if it were an f-string at runtime? ... This is supposedly for security reasons. However, when trying to emulate this behavior that I wanted (and know the security implications of), my solutions will tend to be less secure. Here is what I have been thinking about: If you really want the full power of an f-string, then you're asking for the full power of eval(), and that means all the security implications thereof, not to mention the difficulties of namespacing. Have you considered using the vanilla format() method instead? But if you really REALLY know what you're doing, just use eval() directly. I don't really see what you'd gain from an f-string. At very least, work with a well-defined namespace and eval whatever you need in that context. Maybe, rather than asking for a way to treat a string as code, ask for what you ACTUALLY need, and we can help? ChrisA Fair enough, Chris, but still ISTM that it is reasonable to ask (perhaps for a different use-case) whether there is a way of evaluating a string at runtime as if it were an f-string. We encourage people to ask questions on this list, even though the answer will not always be what they're hoping for. I appreciate that the answer may be "No, because it would be a lot of work - and increase the maintenance burden - to support a relatively rare requirement". Perhaps someone will be inspired to write a function to do it. 😎 Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Evaluation of variable as f-string
Whoa! Whoa! Whoa!
I appreciate the points you are making, Chris, but I am a bit taken
aback by such forceful language.
On 27/01/2023 19:18, Chris Angelico wrote:
On Sat, 28 Jan 2023 at 05:31, Rob Cliffe via Python-list
wrote:
On 23/01/2023 18:02, Chris Angelico wrote:
Maybe, rather than asking for a way to treat a string as code, ask for
what you ACTUALLY need, and we can help?
ChrisA
Fair enough, Chris, but still ISTM that it is reasonable to ask (perhaps
for a different use-case) whether there is a way of evaluating a string
at runtime as if it were an f-string. We encourage people to ask
questions on this list, even though the answer will not always be what
they're hoping for.
No, it's not, because that's the "how do I use X to do Y" problem.
Instead, just ask how to do *what you actually need*. If the best way
to do that is to eval an f-string, then someone will suggest that.
But, much much much more likely, the best way to do it would be
something completely different. What, exactly? That's hard to say,
because *we don't know what you actually need*. All you tell us is
what you're attempting to do, which there is *no good way to achieve*.
If the above is addressed to the OP, I can't answer for him.
If it's addressed to me: How about if I wanted a program (a learning
tool) to allow the user to play with f-strings?
I.e. to type in a string, and then see what the result would be if it
had been an f-string?
I suspect there are other use cases, but I confess I can't think of one
right now.
I appreciate that the answer may be "No, because it would be a lot of
work - and increase the maintenance burden - to support a relatively
rare requirement".
What about: "No, because it's a terrible TERRIBLE idea, requires that
you do things horribly backwards, and we still don't even know what
you're trying to do"?
Perhaps someone will be inspired to write a function to do it. 😎
See, we don't know what "it" is, so it's hard to write a function
that's any better than the ones we've seen.
Again, if this is addressed to the OP: I'm not his keeper. 😁
If it's addressed to me: "it" means a function that will take a string
and evaluate it at runtime as if it were an f-string. Sure, with
caveats and limitations. And indeed Thomas Passim found this partial
solution on Stack Overflow:
def effify(non_f_str: str):
return eval(f'f"""{non_f_str}"""')
Using eval() to construct
an f-string and then parse it is TERRIBLE because:
1) It still doesn't work in general, and thus has caveats like "you
can't use this type of quote character"
2) You would have to pass it a dictionary of variables, which also
can't be done with full generality
3) These are the exact same problems, but backwards, that led to
f-strings in the first place
4) eval is extremely slow and horrifically inefficient.
I understand these limitations. Nonetheless I can conceive that there
may be scenarios where it is an acceptable solution (perhaps the
learning tool program I suggested above).
Addressing your points specifically:
1) I believe the quote character limitation could be overcome. It
would need a fair amount of work, for which I haven't (yet) the time or
inclination.
2) Yes in general you would have to pass it one dictionary, maybe
two. I don't see this as an insuperable obstacle. I am not sure what
you mean by "can't be done with full generality" and perhaps that's not
important.
3) Not sure I understand this.
4) On the fairly rare occasions that I have used eval(), I can't
remember speed ever being a consideration.
For some reason, str.format() isn't suitable, but *you haven't said
why*, so we have to avoid that in our solutions. So, to come back to
your concern:
We encourage people to ask
questions on this list, even though the answer will not always be what
they're hoping for.
Well, yes. If you asked "how can I do X", hoping the answer would be
"with a runtime-evaluated f-string", then you're quite right - the
answer might not be what you were hoping for. But since you asked "how
can I evaluate a variable as if it were an f-string", the only
possible answer is "you can't, and that's a horrible idea".
I hope that I have shown that this is a somewhat dogmatic response.
Don't ask how to use X to do Y. Ask how to do Y.
Good advice.
Best wishes
Rob Cliffe
ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: evaluation question
On 30/01/2023 09:41, [email protected] wrote: On Sun, 29 Jan 2023 23:57:51 -0500 Thomas Passin wrote: On 1/29/2023 4:15 PM, [email protected] wrote: On 2023-01-28, Louis Krupp wrote: On 1/27/2023 9:37 AM, [email protected] wrote: eval("print(123)") 123 Does OP expect the text to come from the eval or from the print? x = print( [i for i in range(1, 10)] ) [1, 2, 3, 4, 5, 6, 7, 8, 9] x (nothing printed) Because print() returns nothing (i.e., the statement x is None is True). I don't understand this. What was the point of the upheaval of converting the print command in python 2 into a function in python 3 if as a function print() doesn't return anything useful? Surely even the length of the formatted string as per C's sprintf() function would be helpful? That's a fair question, or rather 2 fair questions. There is an explanation of why the change was made at https://snarky.ca/why-print-became-a-function-in-python-3/ In brief: (a) the print() function is more flexible and can be used in expressions (b) Python's syntax was simplified by dropping the special syntax used by the print statement. sys.stdout.write() does return the number of characters output (you could use this instead of print() if you need this; remember to add a '\n' character at the end of a line). I guess the option of making print() do the same either was not considered, or was rejected, when print was made a function. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Evaluation of variable as f-string
On 27/01/2023 23:41, Chris Angelico wrote:
On Sat, 28 Jan 2023 at 10:08, Rob Cliffe via Python-list
wrote:
Whoa! Whoa! Whoa!
I appreciate the points you are making, Chris, but I am a bit taken
aback by such forceful language.
The exact same points have already been made, but not listened to.
Sometimes, forceful language is required in order to get people to
listen.
If it's addressed to me: How about if I wanted a program (a learning
tool) to allow the user to play with f-strings?
I.e. to type in a string, and then see what the result would be if it
had been an f-string?
I suspect there are other use cases, but I confess I can't think of one
right now.
Use the REPL, which will happily evaluate f-strings in their original
context, just like any other code would. You're already eval'ing, so
it's exactly what you'd expect. This is not the same thing as "typing
in a string", though - it's typing in code and seeing what the result
would be. (Except to the extent that source code can be considered a
string.)
This is hypothetical, but I might want to work on a platform where the
REPL was not available.
If it's addressed to me: "it" means a function that will take a string
and evaluate it at runtime as if it were an f-string. Sure, with
caveats and limitations.
And that's what I am saying is a terrible terrible idea. It will
evaluate things in the wrong context, it has all the normal problems
of eval, and then it introduces its own unique problems with quote
characters.
With great respect, Chris, isn't it for the OP (or anyone else) to
decide - having been warned of the various drawbacks and limitations -
to decide if it's a terrible idea *for him*? He's entitled to decide
that it's just what *he* needs, and that the drawbacks don't matter *for
him". Just as you're entitled to disagree.
And indeed Thomas Passim found this partial
solution on Stack Overflow:
def effify(non_f_str: str):
return eval(f'f"""{non_f_str}"""')
You can find anything on Stack Overflow. Just because you found it
there doesn't mean it's any good - even if it's been massively
upvoted.
Addressing your points specifically:
1) I believe the quote character limitation could be overcome. It
would need a fair amount of work, for which I haven't (yet) the time or
inclination.
No problem. Here, solve it for this string:
eval_me = ' f"""{f\'\'\'{f"{f\'{1+2}\'}"}\'\'\'}""" '
F-strings can be nested, remember.
I remember it well.
As far as I can see (and I may well be wrong; thinking about this
example made my head hurt 😁) this could be solved if PEP 701 were
implemented (so that f-string expressions can contain backslashes) but
not otherwise.
2) Yes in general you would have to pass it one dictionary, maybe
two. I don't see this as an insuperable obstacle. I am not sure what
you mean by "can't be done with full generality" and perhaps that's not
important.
def func():
... x = 1
... class cls:
... y = 2
... print(f"{x=} {y=}")
... print(locals())
...
func()
x=1 y=2
{'__module__': '__main__', '__qualname__': 'func..cls', 'y': 2}
Thanks for clarifying.
Hm. So 'x' is neither in locals() nor in globals(). Which starts me
wondering (to go off on a tangent): Should there be a nonlocals()
dictionary?
Maybe you don't care. Maybe you do. But locals() is not the same as
"all names currently available in this scope". And, this example is
definitely not something I would recommend, but good luck making this
work with eval:
def func():
... x = 1
... print(f"{(x:=2)}")
... print(x)
...
func()
2
2
... x = 1
... print(eval("(x:=2)", globals(), locals()))
... print(x)
...
func()
2
1
Now that, I have to admit, IS a challenge!
3) Not sure I understand this.
Before f-strings existed, one of the big problems with "just use
str.format_map" was that you can't just pass it locals() to get all
the available names. You also can't eval arbitrary code and expect to
get the same results, even if you pass it globals and locals. And
various other considerations here - the exact issues seen here, but
flipped on their heads. So the obvious question is: why not just use
str.format_map?
What this underlines to me is what a good thing f-strings are. And with
PEP 701 they will be IMO even better.
Just as when you were working on PEP 463 (Exception-catching
expressions) - which I still think would be a Good Thing - some research
I did made me realise how good the existing try/except/else/finally
mechanism actually is. There's lots of Good Stuff in Python. 😁
Best wishes
Rob
--
https://mail.python.org/mailman/listinfo/python-list
Re: evaluation question
On 02/02/2023 09:31, [email protected] wrote: On Wed, 1 Feb 2023 18:28:04 +0100 "Peter J. Holzer" wrote: --b2nljkb3mdefsdhx Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On 2023-02-01 09:00:39 -, [email protected] wrote: Its not evolution, its revolution. Evolution retains old functionality. Tell a penguin that it can fly :-) Yeah ok :) But the ancestors of penguins didn't wake up one morning, flap their wings and fall out the tree, it happened gradually. Python2 syntax could have been retained for X versions of 3 just as C++ keeps old stuff until its eventually deprecated them removed. Yeah? So what would this do: print () In Python 2 this prints an empty tuple. In Python 3 this is a call to the print function with no arguments, which prints a blank line. You can't have it both ways. In any case, supporting two different syntaxes simultaneously would be messy and difficult to maintain. Better a clean break, with Python 2 support continuing for a long time (as it was). Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Evaluation of variable as f-string
[re-sending this to both the list and to Chris, as a prior send to the list only was bounced back] On 31/01/2023 22:33, Chris Angelico wrote: Thanks for clarifying. Hm. So 'x' is neither in locals() nor in globals(). Which starts me wondering (to go off on a tangent): Should there be a nonlocals() dictionary? I don't think so, but there might be some value in a dictionary containing all available variables. It would have the same "don't depend on writing" caveats that locals() has (or would be specifically defined as a copy and thus disconnected), so its value would be limited. And it would probably STILL be imperfect, because perfection would require that it be a compiler construct, due to the way that nonlocals are implemented. Does that mean that it is not possible to have a (built-in) function that would construct and return a dictionary of all available variables and their values? If it were possible, it could be useful, and there would be no impact on Python run-time speed if it were only constructed on demand. Best wishes Rob -- https://mail.python.org/mailman/listinfo/python-list
Re: evaluation question
On 07/02/2023 08:15, Chris Angelico wrote: On Tue, 7 Feb 2023 at 18:49, Rob Cliffe via Python-list wrote: On 02/02/2023 09:31, [email protected] wrote: On Wed, 1 Feb 2023 18:28:04 +0100 "Peter J. Holzer" wrote: --b2nljkb3mdefsdhx Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Content-Transfer-Encoding: quoted-printable On 2023-02-01 09:00:39 -, [email protected] wrote: Its not evolution, its revolution. Evolution retains old functionality. Tell a penguin that it can fly :-) Yeah ok :) But the ancestors of penguins didn't wake up one morning, flap their wings and fall out the tree, it happened gradually. Python2 syntax could have been retained for X versions of 3 just as C++ keeps old stuff until its eventually deprecated them removed. Yeah? So what would this do: print () In Python 2 this prints an empty tuple. In Python 3 this is a call to the print function with no arguments, which prints a blank line. You can't have it both ways. In any case, supporting two different syntaxes simultaneously would be messy and difficult to maintain. There are two solutions to this. The most obvious is "from __future__ import print_function", which gives you the full power and flexibility of Py3 in anything back as far as 2.6; the other is to always pass a single string argument to print: print("") print("spam %d ham %d" % (spam, ham)) This will work in pretty much ANY version of Python [1] and doesn't require any sort of per-module configuration. The idea that old syntax should be retained is only part of the story. While it's definitely important to not break old code unnecessarily, it is far more important to ensure that there's *some way* to write code that works across multiple versions. That's what we have here: even with the breaking changes, there was usually a way to make your code run identically on multiple versions. Sometimes this means a compatibility shim at the top, like "try: raw_input; except NameError: raw_input = input", and sometimes it means following a discipline like putting b"..." for all strings that need to be bytes. But there always needs to be a way. ChrisA [1] This is the part where someone points out to me that it wouldn't work in Python 1.3 or something You are quite right Chris, and indeed I have used both solutions in my own code to keep 2-3 compatibility. I was just pointing out that continuing to support Python 2 syntax in Python 3 was not an option. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Comparing caching strategies
On 11/02/2023 00:39, Dino wrote: First off, a big shout out to Peter J. Holzer, who mentioned roaring bitmaps a few days ago and led me to quite a discovery. I was intrigued to hear about roaring bitmaps and discover they really were a thing (not a typo as I suspected at first). What next, I wonder? argumentative arrays chattering classes (on second thoughts, we have those already) dancing dictionaries garrulous generators laughing lists piping pipelines singing strings speaking sets stuttering sorts talking tuples whistling walruses? The future awaits [pun not intended] ... -- https://mail.python.org/mailman/listinfo/python-list
Re: LRU cache
On 18/02/2023 15:29, Thomas Passin wrote:
On 2/18/2023 5:38 AM, Albert-Jan Roskam wrote:
I sometimes use this trick, which I learnt from a book by Martelli.
Instead of try/except, membership testing with "in"
(__contains__) might
be faster. Probably "depends". Matter of measuring.
def somefunc(arg, _cache={}):
if len(_cache) > 10 ** 5:
_cache.pop()
try:
return _cache[arg]
except KeyError:
result = expensivefunc(arg)
_cache[arg] = result
return result
Albert-Jan
_cache.get(arg) should be a little faster and use slightly fewer
resources than the try/except.
Provided that you can provide a default value to get() which will never
be a genuine "result".
--
https://mail.python.org/mailman/listinfo/python-list
Re: semi colonic
On 23/02/2023 02:04, Thomas Passin wrote: On 2/22/2023 7:58 PM, [email protected] wrote: So can anyone point to places in Python where a semicolon is part of a best or even good way to do anything? I use the semicolon (once in a while) is for quick debugging. I might add as line like, perhaps, import os; print(os.path.exists(filename)) This way I can get rid of the debugging statement by deleting that single line. This is non only quicker but I'm less likely to delete too much by mistake. I do exactly the same. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: LRU cache
On 18/02/2023 17:19, Albert-Jan Roskam wrote:
On Feb 18, 2023 17:28, Rob Cliffe via Python-list
wrote:
On 18/02/2023 15:29, Thomas Passin wrote:
> On 2/18/2023 5:38 AM, Albert-Jan Roskam wrote:
>> I sometimes use this trick, which I learnt from a book by
Martelli.
>> Instead of try/except, membership testing with "in"
>> (__contains__) might
>> be faster. Probably "depends". Matter of measuring.
>> def somefunc(arg, _cache={}):
>> if len(_cache) > 10 ** 5:
>> _cache.pop()
>> try:
>> return _cache[arg]
>> except KeyError:
>> result = expensivefunc(arg)
>> _cache[arg] = result
>> return result
>> Albert-Jan
>
> _cache.get(arg) should be a little faster and use slightly fewer
> resources than the try/except.
>
Provided that you can provide a default value to get() which will
never
be a genuine "result".
=
This might be better than None:
_cache.get(arg, Ellipsis)
A common strategy is to have a dedicated sentinel object. E.g. (untested):
IMPOSSIBLE_RESULT = object()
...
if _cache.get(arg, IMPOSSIBLE_RESULT) == IMPOSSIBLE_RESULT:
# arg was not in the cache
...
--
https://mail.python.org/mailman/listinfo/python-list
Re: Why doesn't Python (error msg) tell me WHAT the actual (arg) values are ?
On 22/02/2023 20:05, Hen Hanna wrote: Python makes programming (debugging) so easy I agree with that! Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: semi colonic
On 23/02/2023 00:58, [email protected] wrote: So can anyone point to places in Python where a semicolon is part of a best or even good way to do anything? Yes. Take this bit of toy code which I just dreamed up. (Of course it is toy code; don't bother telling me how it could be written better.) If it looks a bit ragged, pretend it is in a fixed font. if dow==0: day="Mon"; calcPay() if dow==1: day="Tue"; calcPay() if dow==2: day="Wed"; calcPay() if dow==3: day="Thu"; calcPay() if dow==4: day="Fri"; calcpay() if dow==5: day="Sat"; calcPay(rate=1.5) if dow==6: day="Sun"; calcPay(rate=2) The point is: when you have several short bits of code with an identical or similar pattern, *vertically aligning* the corresponding parts can IMO make it much easier to read the code and easier to spot errors. Compare this: if dow==0: day="Mon" calcPay() if dow==1: day="Tue" calcPay() if dow==2: day="Wed" calcPay() if dow==3: day="Thu" calcPay() if dow==4: day="Fri" calcpay() if dow==5: day="Sat" calcPay(rate=1.5) if dow==6: day="Sun" calcPay(rate=2) Not so easy to spot the mistake now, is it? Not to mention the saving of vertical space. Best wishes Rob Cliffe PS If you really care, I can send you a more complicated example of real code from one of my programs which is HUGELY more readable when laid out in this way. -- https://mail.python.org/mailman/listinfo/python-list
Re: semi colonic
On 23/02/2023 02:25, Hen Hanna wrote: i sometimes put extra commas... as: [ 1, 2, 3, 4, ] That is a good idea. Even more so when the items are on separate lines: [ "spam", "eggs", "cheese", ] and you may want to change the order. so it is (or may be) easier to add things later. --- i can think of putting extra final ; for the same reason. That may not be such a good idea. Writing multiple statements on one line is generally discouraged (notwithstanding that IMO it is occasionally appropriate). Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Line continuation and comments
On 22/02/2023 15:23, Paul Bryan wrote: Adding to this, there should be no reason now in recent versions of Python to ever use line continuation. Black goes so far as to state "backslashes are bad and should never be used": https://black.readthedocs.io/en/stable/the_black_code_style/future_style.html#using-backslashes-for-with-statements def someFunc(): HelpText = """\ Left click: Open spam Shift + Left click: Cook spam Right click: Crack egg Shift + Right click: Fry egg """ The initial backslash aligns the first line with the others (in a fixed font of course). Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Python 3.10 Fizzbuzz
On 27/02/2023 21:04, Ethan Furman wrote: On 2/27/23 12:20, rbowman wrote: > "By using Black, you agree to cede control over minutiae of hand- > formatting. In return, Black gives you speed, determinism, and freedom > from pycodestyle nagging about formatting. You will save time and mental > energy for more important matters." > > Somehow I don't think we would get along very well. I'm a little on the > opinionated side myself. I personally cannot stand Black. It feels like every major choice it makes (and some minor ones) are exactly the opposite of the choice I make. -- ~Ethan~ I've never tried Black or any other code formatter, but I'm sure we wouldn't get on. -- https://mail.python.org/mailman/listinfo/python-list
Re: How to fix this issue
On 01/03/2023 18:46, Thomas Passin wrote: If this is what actually happened, this particular behavior occurs because Python on Windows in a console terminates with a instead of the usual . I think you mean . -- https://mail.python.org/mailman/listinfo/python-list
Packing Problem
I found Hen Hanna's "packing" problem to be an intriguing one: Given a list of words: ['APPLE', 'PIE', 'APRICOT', 'BANANA', 'CANDY'] find a string (in general non-unique) as short as possible which contains the letters of each of these words, in order, as a subsequence. It struck me as being rather hard for a homework problem, unless I'm missing something blindingly obvious. Here is what I came up with (I could have done with removeprefix/removesuffix but I'm stuck on Python 3.8 for now 🙁): # Pack.py def Pack(Words): if not Words: return '' # The method is to build up the result by adding letters at the beginning # and working forward, and by adding letters at the end, working backwards, # meanwhile shrinking the words in the list. Words = list(Words) # Don't mutate the original Initial = '' Final = '' while True: AnyProgress = False # It is safe to add an initial letter (of one or more of the words) if # EITHER There is no word that contains it as # a non-initial letter but not as an initial letter. # OR All words start with it. while True: FirstLetters = ''.join(w[0] for w in Words) FirstLetters = [ ch for ch in FirstLetters if all(w[0]==ch for w in Words) or not any(ch in w[1:] and w[0]!=ch for w in Words) ] if not FirstLetters: break AnyProgress = True ch = FirstLetters[0] # Pick one Initial += ch # Build up the answer from the beginning Words = [ (w[1:] if w[0]==ch else w) for w in Words ] Words = [ w for w in Words if w != ''] if not Words: return Initial + Final # It is safe to add a final letter (of one or more of the words) of # EITHER There is no word that contains it as # a non-final letter but not as a final letter. # OR All words end with it. while True: LastLetters = ''.join(w[-1] for w in Words) LastLetters = [ ch for ch in LastLetters if all(w[-1]==ch for w in Words) or not any(ch in w[:-1] and w[-1]!=ch for w in Words) ] if not LastLetters: break AnyProgress = True ch = LastLetters[0] # Pick one Final = ch + Final # Build up the answer from the end Words = [ (w[:-1] if w[-1]==ch else w) for w in Words ] Words = [ w for w in Words if w != ''] if not Words: return Initial + Final if not AnyProgress: break # Try all the possibilities for the next letter to add at the beginning, # with recursive calls, and pick one that gives a shortest answer: BestResult = None for ch in set(w[0] for w in Words): Words2 = list( (w[1:] if w[0] == ch else w) for w in Words ) Words2 = [ w for w in Words2 if w != '' ] res = ch + Pack(Words2) if BestResult is None or len(res) < len(BestResult): BestResult = res return Initial + BestResult + Final print(Pack(['APPLE', 'PIE', 'APRICOT', 'BANANA', 'CANDY'])) The output: BAPPRICNANADYOTLE which has the same length as the answer I came up with trying to solve it with my unaided brain, which may or may not be reassuring 😂, and also contains a much-needed BRANDY. I expect there are simpler and more efficient solutions. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Packing Problem
Slightly improved version (deals with multiple characters together instead of one at a time): # Pack.py def Pack(Words): if not Words: return '' # The method is to build up the result by adding letters at the beginning # and working forward, and by adding letters at the end, working backwards, # meanwhile shrinking the words in the list. Words = list(Words) # Don't mutate the original Initial = '' Final = '' while True: # It is safe to add an initial letter (of one or more of the words) if # EITHER There is no word that contains it as # a non-initial letter but not as an initial letter. # OR All words start with it. while True: FirstLetters = set(w[0] for w in Words) FirstLettersSafe = sorted(ch for ch in FirstLetters if all(w[0]==ch for w in Words) or not any(ch in w[1:] and w[0]!=ch for w in Words)) # sorted() to make the answer deterministic # (not dependent on set ordering) if not FirstLettersSafe: break AnyProgress = True Initial += ''.join(FirstLettersSafe) # Build up the answer from the beginning Words = [ (w[1:] if w[0] in FirstLettersSafe else w) for w in Words ] Words = [ w for w in Words if w != ''] if not Words: return Initial + Final # It is safe to add a final letter (of one or more of the words) of # EITHER There is no word that contains it as # a non-final letter but not as a final letter. # OR All words end with it. while True: LastLetters = set(w[-1] for w in Words) LastLettersSafe = sorted(ch for ch in LastLetters if all(w[-1]==ch for w in Words) or not any(ch in w[:-1] and w[-1]!=ch for w in Words)) # sorted() to make the answer deterministic # (not dependent on set ordering) if not LastLettersSafe: break Final = ''.join(LastLettersSafe) + Final # Build up the answer from the end Words = [ (w[:-1] if w[-1] in LastLettersSafe else w) for w in Words ] Words = [ w for w in Words if w != ''] if not Words: return Initial + Final if not (FirstLettersSafe or LastLettersSafe): break # stuck # Try all the possibilities for the next letter to add at the beginning, # with recursive calls, and pick one that gives a shortest answer: BestResult = None for ch in FirstLetters: Words2 = list( (w[1:] if w[0] == ch else w) for w in Words ) Words2 = [ w for w in Words2 if w != '' ] res = ch + Pack(Words2) if BestResult is None or len(res) < len(BestResult): BestResult = res return Initial + BestResult + Final print(Pack(['APPLE', 'PIE', 'APRICOT', 'BANANA', 'CANDY'])) Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Cryptic software announcements (was: ANN: DIPY 1.6.0)
On 01/03/2023 00:13, Peter J. Holzer wrote: [This isn't specifically about DIPY, I've noticed the same thing in other announcements] On 2023-02-28 13:48:56 -0500, Eleftherios Garyfallidis wrote: Hello all, We are excited to announce a new release of DIPY: DIPY 1.6.0 is out from the oven! That's nice, but what is DIPY? In addition, registration for the oceanic DIPY workshop 2023 (April 24-28) is now open! Our comprehensive program is designed to equip you with the skills and knowledge needed to master the latest techniques and tools in structural and diffusion imaging. Ok, so since the workshop is about ".., tools in structural and diffusion imaging", DIPY is probably such a tool. However, without this incidental announcement I wouldn't have any idea what it is or if it would be worth my time clicking at any of the links. I think it would be a good idea if software announcements would include a single paragraph (or maybe just a single sentence) summarizing what the software is and does. hp +1 Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Cutting slices
On 05/03/2023 22:59, aapost wrote: On 3/5/23 17:43, Stefan Ram wrote: The following behaviour of Python strikes me as being a bit "irregular". A user tries to chop of sections from a string, but does not use "split" because the separator might become more complicated so that a regular expression will be required to find it. But for now, let's use a simple "find": |>>> s = 'alpha.beta.gamma' |>>> s[ 0: s.find( '.', 0 )] |'alpha' |>>> s[ 6: s.find( '.', 6 )] |'beta' |>>> s[ 11: s.find( '.', 11 )] |'gamm' |>>> . The user always inserted the position of the previous find plus one to start the next "find", so he uses "0", "6", and "11". But the "a" is missing from the final "gamma"! And it seems that there is no numerical value at all that one can use for "n" in "string[ 0: n ]" to get the whole string, isn't it? The final `find` returns -1 because there is no separator after 'gamma'. So you are asking for s[ 11 : -1] which correctly returns 'gamm'. You need to test for this condition. Alternatively you could ensure that there is a final separator: s = 'alpha.beta.gamma.' but you would still need to test when the string was exhausted. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: =- and -= snag
On 14/03/2023 21:28, [email protected] wrote: TThere are people now trying to in some ways ruin the usability by putting in type hints that are ignored and although potentially helpful as in a linter evaluating it, instead often make it harder to read and write code if required to use it. +1 Whenever I see code with type hints, I have to edit them out, either mentally, or physically, to understand what the code is actually doing. It's adding new syntax which I'm not used to and don't want to be forced to learn. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Packing Problem
On 02/03/2023 19:40, Weatherby,Gerard wrote: Haven’t look at it all in detail, but I’d replace the list comprehensions with a loop: CopyOfWords = list(Words) Words = [(w[1:] if w[0] == ch else w) for w in Words] Words = [w for w in Words if w != ''] nextWords = [] for w in CopyOfWords: if w[0] != ch: nextWords.append(w) elif len(w) > 1: nextWords.append(w[1:]) assert Words == nextWords Why? Rob Cliffe From: Python-list on behalf of Rob Cliffe via Python-list Date: Thursday, March 2, 2023 at 2:12 PM To: [email protected] Subject: Re: Packing Problem *** Attention: This is an external email. Use caution responding, opening attachments or clicking on links. *** Slightly improved version (deals with multiple characters together instead of one at a time): # Pack.py def Pack(Words): if not Words: return '' # The method is to build up the result by adding letters at the beginning # and working forward, and by adding letters at the end, working backwards, # meanwhile shrinking the words in the list. Words = list(Words) # Don't mutate the original Initial = '' Final = '' while True: # It is safe to add an initial letter (of one or more of the words) if # EITHERThere is no word that contains it as # a non-initial letter but not as an initial letter. # OR All words start with it. while True: FirstLetters = set(w[0] for w in Words) FirstLettersSafe = sorted(ch for ch in FirstLetters if all(w[0]==ch for w in Words) or not any(ch in w[1:] and w[0]!=ch for w in Words)) # sorted() to make the answer deterministic # (not dependent on set ordering) if not FirstLettersSafe: break AnyProgress = True Initial += ''.join(FirstLettersSafe) # Build up the answer from the beginning Words = [ (w[1:] if w[0] in FirstLettersSafe else w) for w in Words ] Words = [ w for w in Words if w != ''] if not Words: return Initial + Final # It is safe to add a final letter (of one or more of the words) of # EITHERThere is no word that contains it as # a non-final letter but not as a final letter. # OR All words end with it. while True: LastLetters = set(w[-1] for w in Words) LastLettersSafe = sorted(ch for ch in LastLetters if all(w[-1]==ch for w in Words) or not any(ch in w[:-1] and w[-1]!=ch for w in Words)) # sorted() to make the answer deterministic # (not dependent on set ordering) if not LastLettersSafe: break Final = ''.join(LastLettersSafe) + Final # Build up the answer from the end Words = [ (w[:-1] if w[-1] in LastLettersSafe else w) for w in Words ] Words = [ w for w in Words if w != ''] if not Words: return Initial + Final if not (FirstLettersSafe or LastLettersSafe): break # stuck # Try all the possibilities for the next letter to add at the beginning, # with recursive calls, and pick one that gives a shortest answer: BestResult = None for ch in FirstLetters: Words2 = list( (w[1:] if w[0] == ch else w) for w in Words ) Words2 = [ w for w in Words2 if w != '' ] res = ch + Pack(Words2) if BestResult is None or len(res) < len(BestResult): BestResult = res return Initial + BestResult + Final print(Pack(['APPLE', 'PIE', 'APRICOT', 'BANANA', 'CANDY'])) Rob Cliffe -- https://urldefense.com/v3/__https://mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!l3ysx0BUPZBdKdwb9F8mw4BAE2UIflvNqWeZLfALY2kjEo9e4KTy6fEYoGCTileOUtYe0yp6nL18ymdOguC3TGagEA$<https://urldefense.com/v3/__https:/mail.python.org/mailman/listinfo/python-list__;!!Cn_UX_p3!l3ysx0BUPZBdKdwb9F8mw4BAE2UIflvNqWeZLfALY2kjEo9e4KTy6fEYoGCTileOUtYe0yp6nL18ymdOguC3TGagEA$> -- https://mail.python.org/mailman/listinfo/python-list
Re: Question regarding unexpected behavior in using __enter__ method
This puzzled me at first, but I think others have nailed it. It is not to do with the 'with' statement, but with the way functions are defined. When a class is instantiated, as in x=X(): the instance object gets (at least in effect), as attributes, copies of functions defined *in the class* (using def or lambda) but they become "bound methods", i.e. bound to the instance. Whenever they are called, they will be called with the instance as the first argument, aka self: class X(object): def func(*args, **kargs): pass x = X() y = () x.func and y.func are two *different" functions. When x.func is called, x is added as the first argument. When y.func is called. y is added as the first argument. boundFunc = y.func boundFunc() # Adds y as first argument. Indeed, these functions have an attribute called __self__ whose value is ... you guessed it ... the object they are bound to When a function is defined outside of a class, it remains a simple function, not bound to any object. It does not have a __self__ attribute. Neither does a built-in type such as 'int'. Nor for that matter does the class function X.func: X.func() # Called with no arguments Best wishes Rob Cliffe On 20/04/2023 23:44, Lorenzo Catoni wrote: Dear Python Mailing List members, I am writing to seek your assistance in understanding an unexpected behavior that I encountered while using the __enter__ method. I have provided a code snippet below to illustrate the problem: ``` class X: ... __enter__ = int ... __exit__ = lambda *_: None ... with X() as x: ... pass ... x 0 ``` As you can see, the __enter__ method does not throw any exceptions and returns the output of "int()" correctly. However, one would normally expect the input parameter "self" to be passed to the function. On the other hand, when I implemented a custom function in place of the __enter__ method, I encountered the following TypeError: ``` def myint(*a, **kw): ... return int(*a, **kw) ... class X: ... __enter__ = myint ... __exit__ = lambda *_: None ... with X() as x: ... pass ... Traceback (most recent call last): File "", line 1, in File "", line 2, in myint TypeError: int() argument must be a string, a bytes-like object or a real number, not 'X' ``` Here, the TypeError occurred because "self" was passed as an input parameter to "myint". Can someone explain why this unexpected behavior occurs only in the latter case? I tested this issue on the following Python versions, and the problem persists on all of them: - Python 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0] on linux - Python 3.10.10 (main, Feb 8 2023, 14:50:01) [GCC 9.4.0] on linux - Python 3.10.7 (tags/v3.10.7:6cc6b13, Sep 5 2022, 14:08:36) [MSC v.1933 64 bit (AMD64)] on win32 I appreciate any input or insights that you might have on this matter. Thank you for your help in advance! Best regards, Lorenzo Catoni -- https://mail.python.org/mailman/listinfo/python-list
Learning tkinter
I am trying to learn tkinter. Several examples on the internet refer to a messagebox class (tkinter.messagebox). But: Python 3.8.3 (tags/v3.8.3:6f8c832, May 13 2020, 22:20:19) [MSC v.1925 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information. >>> import tkinter >>> tkinter.messagebox Traceback (most recent call last): File "", line 1, in AttributeError: module 'tkinter' has no attribute 'messagebox' >>> Why is this? TIA Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Addition of a .= operator
On 20/05/2023 18:54, Alex Jando wrote: I have many times had situations where I had a variable of a certain type, all I cared about it was one of it's methods. For example: import hashlib hash = hashlib.sha256(b'word') hash = hash.hexdigest() import enum class Number(enum.Enum): One: int = 1 Two: int = 2 Three: int = 3 num = Number.One num = num.value Now to be fair, in the two situations above, I could just access the method right as I declare the object, however, sometimes when passing values into functions, it's a lot messier to do that. So what I'm suggesting is something like this: import hashlib hash = hashlib.sha256(b'word') hash.=hexdigest() import enum class Number(enum.Enum): One: int = 1 Two: int = 2 Three: int = 3 num = Number.One num.=value It seems to me that this would encourage bad style. When you write num = num.value you are using num with two different meanings (an object and an attribute of it). The original object is lost. If down the line you needed to access another attribute of the object, you would have to backtrack: val = num.value spam = num.spam or at least write the statements in the right order, so that you only overwrite num at the end: spam = num.spam num = num.value Either way, you are making the code more fragile i.e. harder to maintain. Now of course, in your use case it may be perfectly appropriate to write "num = num.value" as you did. But IMO it's not something that should be encouraged in general. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Addition of a .= operator
This sort of code might be better as a single expression. For example:
user = (
request.GET["user"]
.decode("utf-8")
.strip()
.lower()
)
user = orm.user.get(name=user)
LOL. And I thought I was the one with a (self-confessed) tendency to
write too slick, dense, smart-alec code. 😁
Indeed, I was itching to shorten it (starting with the low-hanging
fruit: user = user.strip().lower() ).
Seriously though: this kind of condensation can come unstuck when any of
the steps need to be made more complicated.
(Suppose request.GET might return ASCII, might return Unicode, depending
on which server it was talking to.)
Peter's actual code feels more Pythonic to me. (It's even 2 lines
shorter! 😎)
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Addition of a .= operator
On 23/05/2023 22:03, Peter J. Holzer wrote:
On 2023-05-21 20:30:45 +0100, Rob Cliffe via Python-list wrote:
On 20/05/2023 18:54, Alex Jando wrote:
So what I'm suggesting is something like this:
hash = hashlib.sha256(b'word')
hash.=hexdigest()
num = Number.One
num.=value
It seems to me that this would encourage bad style. When you write
num = num.value
you are using num with two different meanings (an object and an
attribute of it).
I think that's ok if it's the same thing at a high level.
I sometimes have a chain of transformations (e.g. first decode it, then
strip extra spaces, then normalize spelling, then look it up in a
database and replace it with the record, ...). Technically, of course
all these intermediate objects are different, and I could make that
explicit by using different variable names:
user_param = request.GET["user"]
user_decoded = str(user_param, encoding="utf-8")
user_stripped = user_decoded.strip()
user_normalized = user_stripped.lower()
user_object = orm.user.get(name=user_normalized)
But I find it easier to read if I just reuse the same variable name:
user = request.GET["user"]
user = str(user, encoding="utf-8")
user = user.strip()
user = user.lower()
user = orm.user.get(name=user)
Each instance only has a livetime of a single line (or maybe two or
three lines if I have to combine variables), so there's little risk of
confusion, and reusing the variable name makes it very clear that all
those intermediate results are gone and won't be used again.
hp
Quite so. I did imply in my full answer that this kind of thing can be
appropriate in the right context. I'm sure I've done it myself.
Your code is a beautiful example of when such "level-crossing" IS
appropriate; creating a plethora of variables with a short useful life
would be a distraction which HINDERS readability ("Are any of these
variables used later on?").
(Not to mention the effort of thinking up suitable variable names, and
the effort of someone reading the code to assimilate them.)
But IMO your code would not really benefit from writing
user .= strip()
user .= lower()
and allowing it would have the disadvantage (apart from all the known
costs of adding a new feature to Python) of encouraging bad style in the
cases where it IS bad style.
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Tkinter docs?
I have recently started converting a large project to tkinter, starting
with zero knowledge of tkinter. (You are free to think: BAD IDEA. 😁)
I am well aware that adopting a new tool always involves a learning
curve, and that one is prone to think that things are more difficult
than they are/should be, and to belly-ache about it, and that in a year
from now I'll probably wonder what I'm fussing about.
Nonetheless ISTM that there is a particular problem with tkinter:
There doesn't seem to be any decent documentation for it anywhere.
I seem to have spent al least 95% (feels more like 99%) of my time in
research, only the rest available for coding. Everything I've learned
has come from scouring the Internet for Stack Overflow pages, videos,
and other articles. And while I'm VERY grateful for these resources,
most of them cover very basic use, and if I want information on some
more obscure technical point, I can only go on looking and looking and
hope I eventually stumble upon what I'm looking for, or some acceptable
substitute.
FWIW: The tkinter error messages are sometimes helpful, sometimes not,
occasionally very helpful (as when I pass an invalid option parameter to
a function and it tells me what the valid ones are). I feel there is
plenty of room for improvement.
One example for those familiar with tkinter (I assure you there are
others) of how I struggle without adequate documentation:
I had learned very early on that tkinter provides two versions of
some classes: the original, or "tk" versions, and a later generation,
the "ttk" versions (and I have to say that that was a turn-off, as it
seemed that the learning curve has just got steeper). It was surely not
appropriate for me to delve deeply into this issue at that stage, as
there were so many other things I didn't know (like, practically
everything). I decide to go with the "tk" versions pro tem, and to
investigate the "ttk" versions later if it seemed like a good idea. One
annoyance is that some webpages are relevant to one version, some to the
other, almost always without being explicit about which. (Can't blame
the "tk" ones, as they were probably written before "ttk" was invented.)
I was writing a subclass of the Checkbutton class (tkinter's name
for what I call a checkbox). I needed to be able to (1) set (2) clear
(3) interrogate the checked state. This is easy to do if you associate
a "variable" with each Checkbutton instance, as seems to be usual
tkinter practice. ("variable" is not a variable in the usual sense, but
an object provided by tkinter that is linked to a GUI object (such as a
Checkbutton), so that when the GUI object changes, the value of the
"variable" changes, and vice versa.) However, I decided that I wanted to
dispense with the variable, if possible. (My motivation? Simpler
code. Shorter code. Avoiding using up resources by creating
unnecessary objects. You are free to think that I was misguided. You
are free to think that I should have been happy with something that
worked.) I didn't care whether I used a tk.Checkbutton or a
ttk.Checkbutton.
From various Internet articles I discovered (slowly, after wading
through many articles that DID use a "variable"):
A way of GETting the state of a tk.CheckButton (but not a
ttk.CheckButton)
A way of SETting the state of a ttk.CheckButton (but not a
tk.CheckButton)
Or the other way round. Or something else. I can no longer
remember, and I didn't keep a record of all my trials and tribulations,
and I can no longer trace which articles I read when.
EVENTUALLY I discovered:
For a ttk.CheckButton (only), where cb is the checkbox
cb.state() returns a tuple of strings which contains, or
doesn't, "selected", according to the checked state
cb.state(["selected"]) sets the checked state
cb.state(["!selected"]) clears the checked state
"Aha!" I thought. "Problem solved". Gleefully I wrote my code and
tested it.
Er, well, not quite. When the Checkbutton object was instantiated, it
showed neither a checked nor an unchecked box, but a solid black box.
This was the third or so-called "alternate" state provided by tkinter.
(Gee, thanks. Oh sorry, it's probably not your fault; as I understand
it, tkinter, you're really just a wrapper.)
Further research revealed that I could get past this by writing
cb.state(["!alternate", "!selected"])
when the object was instantiated.
Phew! Finally got my code working.
But the story is not quite over. To get the checked state I was using
"selected" in cb.state()
While this works, later (in fact while composing this e-mail) I stumbled
across
cb.instate(["selected"])
which does the same thing but is, I guess, the preferred method. I have
amended my code accordingly.
(I don't know why the square brackets are necessary, but
cb.instate("selected")
doesn't work.)
Sigh. I suppose I have t
Re: Tkinter docs?
Thanks to everyone who replied. All replies were constructive, none were telling me to stop belly-aching. I forgot/omitted to state that it was I who wrote the original project (in a completely different language), making the task of re-writing it much less formidable. And meaning that I am familiar with the general concepts of building a GUI. Still, it will be a lot of work. Grant, I may well buy one of the books you suggested. I find the topic of themes and styles the hardest one to get my head around (if anyone knows a good introduction that would be fantastic). All the other stuff is OK provided I can find something on the net to give me the necessary information, which so far I usually can. Christian, I am adopting your suggestion of using ttk widgets, except for Button objects, because The colour of tk.Button objects can be set directly (bg=... fg=...) On my platform (Windows10) the shadowing of tk.Button objects is more conspicuous (without using styles or whatever). Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
divmod with negative Decimal values
I am using Python 3.11.4.
Can anyone explain why Decimal values behave differently from ints when
negative values are used in divmod as follows:
>>> divmod(-1, 60)
(-1, 59) # as expected
>>> divmod(Decimal("-1"), 60)
(Decimal('-0'), Decimal('-1'))
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
f-string error message
I am currently using Python 3.11.4.
First I want to say: f-strings are great! I use them all the time,
mostly but by no means exclusively for debug messages. And in 3.12 they
will get even better.
And the improved error messages in Python (since 3.9) are great too!
Keep up the good work.
However the following error message confused me for a while when it
happened in real code:
import decimal
x=42
f"{x:3d}"
' 42'
x=decimal.Decimal('42')
f"{x:3d}"
Traceback (most recent call last):
File "", line 1, in
ValueError: invalid format string
I understand that this is an error: I'm telling the f-string to expect
an integer when in fact I'm giving it a Decimal.
And indeed f"{x:3}" gives ' 42' whether x is an int or a Decimal.
However, to my mind it is not the format string that is invalid, but the
value supplied to it.
Would it be possible to have a different error message, something like
ValueError: int expected in format string but decimal.Decimal found
Or am I missing something?
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter ttk Treeview binding responds to past events!
On 11/09/2023 21:25, Mirko via Python-list wrote:
Am 11.09.23 um 14:30 schrieb John O'Hagan via Python-list:
I was surprised that the code below prints 'called' three times.
from tkinter import *
from tkinter.ttk import *
root=Tk()
def callback(*e):
print('called')
tree = Treeview(root)
tree.pack()
iid = tree.insert('', 0, text='test')
tree.selection_set(iid)
tree.selection_remove(iid)
tree.selection_set(iid)
tree.bind('<>', callback)
mainloop()
In other words, selection events that occurred _before_ the callback
function was bound to the Treeview selections are triggering the
function upon binding. AFAIK, no other tk widget/binding combination
behaves this way (although I haven't tried all of them).
This was a problem because I wanted to reset the contents of the
Treeview without triggering a relatively expensive bound function, but
found that temporarily unbinding didn't prevent the calls.
I've worked around this by using a regular button-click binding for
selection instead, but I'm curious if anyone can cast any light on
this.
Cheers
John
AFAIK (it's been quite some time, since I used Tk/Tkinter):
These selection events are not triggered upon binding, but after the
mainloop has startet. Tk's eventloop is queue-driven, so the
tree.selection_{set,remove}() calls just place the events on the
queue. After that, you setup a callback and when the mainloop starts,
it processes the events from the queue, executing the registered
callback.
I seem to remember, that I solved a similar issue by deferring the
callback installation using root.after().
from tkinter import *
from tkinter.ttk import *
root=Tk()
def callback(*e):
print('called')
tree = Treeview(root)
tree.pack()
iid = tree.insert('', 0, text='test')
tree.selection_set(iid)
tree.selection_remove(iid)
tree.selection_set(iid)
root.after(100, lambda: tree.bind('<>', callback))
mainloop()
This does not print "called" at all after startup (but still selects
the entry), because the callback has not been installed when the
mainloop starts. But any subsequent interaction with the list
(clicking) will print it (since the callback is then setup).
HTH
Indeed. And you don't need to specify a delay of 100 milliseconds. 0
will work (I'm guessing that's because queued actions are performed in
the order that they were queued).
I have also found that after() is a cure for some ills, though I avoid
using it more than I have to because it feels ... a bit fragile, perhaps.
E.g. suppose the mouse is clicked on a widget and tk responds by giving
that widget the focus, but I don't want that to happen.
I can't AFAIK prevent the focus change, but I can immediately cancel it with
X.after(0, SomeOtherWidget.focus_set)
where X is any convenient object with the "after" method (just about any
widget, or the root).
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Tkinter ttk Treeview binding responds to past events!
On 12/09/2023 19:51, Mirko via Python-list wrote: I have also found that after() is a cure for some ills, though I avoid using it more than I have to because it feels ... a bit fragile, perhaps. Yeah. Though for me it was the delay which made it seem fragile. With a 0 delay, this looks much more reliable. At one point I found myself writing, or thinking of writing, this sort of code after(1, DoSomeThing) after(2, Do SomeThingElse) after(3, DoAThirdThing) ... but this just felt wrong (is it reliable if some Things take more than a millisecond? It may well be; I don't know), and error-prone if I want to add some more Things. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: []=[]
It's not a bug, it's an empty unpacking. Just as you can write [A,B] = [1,2] # Sets A to 1, B to 2 Best wishes Rob Cliffe On 23/09/2023 04:41, Greg Ewing via Python-list wrote: On 23/09/23 4:51 am, Stefan Ram wrote: []=[] (Executes with no error.) # []=[] ( 1 ) #\_/# (Executes with no error.) -- https://mail.python.org/mailman/listinfo/python-list
Writing to clipboard in Python 3.11
Recently I switched from Python 3.8.3 to Python 3.11.4. A strange
problem appeared which was not there before:
I am using the win32clipboard backage (part of pywin32), and when I use
SetClipboardData() to write text which consists ENTIRELY OF DIGITS to
the clipboard, I either get an error (not always the same error message)
or a program crash. The problem does not appear if I use
SetClipboardText() instead.
Sample program:
from win32clipboard import *
OpenClipboard()
SetClipboardData(CF_UNICODETEXT, "A")
SetClipboardData(CF_UNICODETEXT, "A0")
SetClipboardData(CF_UNICODETEXT, "0A")
SetClipboardText("0", CF_UNICODETEXT)
print("OK so far")
SetClipboardData(CF_UNICODETEXT, "0")
CloseClipboard()
Sample output:
OK so far
Traceback (most recent call last):
File "R:\W.PY", line 8, in
SetClipboardData(CF_UNICODETEXT, "0")
pywintypes.error: (0, 'SetClipboardData', 'No error message is available')
I can get round the problem by using SetClipboardText(). But can anyone
shed light on this?
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Beep on WIndows 11
Apologies if this is not a Python question.
I recently moved from a WIndows 10 laptop to a Windows 11 one.
Although there is nothing wrong with the sound on the new machine (I can
listen to podcasts and watch videos), I find that outputting "\a" to the
console (aka stdout) no longer beeps (or makes any sound). This is true
whether I print "\a" from a python program, or "type
".
I have found via Google workarounds such as
os.system("rundll32 user32.dll,MessageBeep")
but it is a trifle annoying to have to modify all of my programs that beep.
Can anyone shed light on this, and perhaps give a simpler fix?
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: why sqrt is not a built-in function?
On 14/01/2021 17:44, Denys Contant wrote: I don't understand why sqrt is not a built-in function. Why do we have to first import the function from the math module? I use it ALL THE TIME! I agree that, especially if you have experience in other languages, this feels odd, and I have some sympathy for you. I am not sure that I can give you a 100% satisfactory answer. But: if the math module should be automatically imported, how many other modules that _somebody_ uses "all the time" should also be automatically be imported? Python gives you a light-weight framework (fast to load a program) with the option to plug in whichever batteries you need, and it's not so terrible to remember to write "import math" or similar, especially after the first time. That felt good. Thank you. Although I hope you have other ways of venting your frustration than shouting at newsgroups. Perhaps juggling balls? Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
a + not b
I can't work out why 1 + - 1 1 + (not 1) are legal syntax, but 1 + not 1 isn't. Is there a good reason for this? Thanks Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
REPL peculiarity
This is a valid Python program: def f(): pass print(f) But at the REPL: >>> def f(): pass ... print(f) File "", line 2 print(f) ^ SyntaxError: invalid syntax It doesn't seem to matter what the second line is. In the REPL you have to leave a blank line after the "def" line. Why? Tested am using Python 3.8.3 and 2.7.18. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: memory consumption
On 31/03/2021 09:35, Alexey wrote: среда, 31 марта 2021 г. в 01:20:06 UTC+3, Dan Stromberg: What if you increase the machine's (operating system's) swap space? Does that take care of the problem in practice? I can`t do that because it will affect other containers running on this host. In my opinion it may significantly reduce their performance. Probably still worth trying. Always better to measure than to guess. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Friday Finking: initialising values and implied tuples
On 02/04/2021 23:10, dn via Python-list wrote: (f) the space-saver: resource = "Oil"; time = 1; crude = 2; residue = 3; my_list = "long" IMO This can be OK when the number of items is VERY small (like 2) and not expected to increase (or decrease). Especially if there are multiple similar initialisations: x = 1; y = 2 ... x = 2 ; y = 6 This saves vertical space and the similarity of the initialisations may be more apparent. It also means only a single line need be cut/pasted/modified. No doubt others will disagree. This answer reflects my willingness to use multiple statements on a line OCCASIONALLY, WHEN I think it is appropriate (as I have said or implied in other posts), whereas many people seem to regard this as completely taboo. Another example: x1 = 42; y1 = 3; z1 = 10 x2 = 41; y2 = 12; z2 = 9 x3 = 8; y3 = 8; z3 = 10 (please imagine it's in a fixed font with everything neatly vertically aligned). This has see-at-a-glance STRUCTURE: the letters are aligned vertically and the "subscripts" horizontally. Write it as 9 lines and it becomes an amorphous mess in which mistakes are harder to spot. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Friday Finking: initialising values and implied tuples
On 03/04/2021 04:09, [email protected] wrote: On 2021-04-03 at 02:41:59 +0100, Rob Cliffe via Python-list wrote: x1 = 42; y1 = 3; z1 = 10 x2 = 41; y2 = 12; z2 = 9 x3 = 8; y3 = 8; z3 = 10 (please imagine it's in a fixed font with everything neatly vertically aligned). This has see-at-a-glance STRUCTURE: the letters are aligned vertically and the "subscripts" horizontally. Write it as 9 lines and it becomes an amorphous mess in which mistakes are harder to spot. I agree that writing it as 9 lines is an accident waiting to happen, but if you must see that structure, then go all in: (x1, y1, z1) = (43, 3, 10) (x2, y2, z2) = (41, 12, 9) (x3, y3, z3) = ( 8, 8, 10) Agreed, that is even easier to read. (It would be kinda nice if the compiler could optimise the tuples away, for those of us who are paranoid about performance.) Or even: ((x1, y1, z1) (x2, y2, z2) (x3, y3, z3)) = ((43, 3, 10) (41, 12, 9) ( 8, 8, 10)) Or not. YMMV. I guess this doesn't come up enough in my own code to worry about it. -- https://mail.python.org/mailman/listinfo/python-list
Re: Friday Finking: initialising values and implied tuples
On 05/04/2021 00:47, dn via Python-list wrote: On 04/04/2021 01.00, Rob Cliffe via Python-list wrote: On 03/04/2021 04:09, [email protected] wrote: On 2021-04-03 at 02:41:59 +0100, Rob Cliffe via Python-list wrote: x1 = 42; y1 = 3; z1 = 10 x2 = 41; y2 = 12; z2 = 9 x3 = 8; y3 = 8; z3 = 10 (please imagine it's in a fixed font with everything neatly vertically aligned). This has see-at-a-glance STRUCTURE: the letters are aligned vertically and the "subscripts" horizontally. Write it as 9 lines and it becomes an amorphous mess in which mistakes are harder to spot. I agree that writing it as 9 lines is an accident waiting to happen, but if you must see that structure, then go all in: (x1, y1, z1) = (43, 3, 10) (x2, y2, z2) = (41, 12, 9) (x3, y3, z3) = ( 8, 8, 10) Agreed, that is even easier to read. (It would be kinda nice if the compiler could optimise the tuples away, for those of us who are paranoid about performance.) I think I've read that the compiler is smart-enough to realise that the RHS 'literal-tuples'?'tuple-literals' are being used as a 'mechanism', and thus the inits are in-lined. Apologies: I can't find a web.ref. Likely one of our colleagues, who is 'into' Python-internals, could quickly clarify... [snip] It doesn't appear to, at least not always. In Python 3.8.3: from dis import dis def f(): x = 1 ; y = 2 def g(): (x,y) = (1,2) dis(f) dis(g) Output: 2 0 LOAD_CONST 1 (1) 2 STORE_FAST 0 (x) 4 LOAD_CONST 2 (2) 6 STORE_FAST 1 (y) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE 3 0 LOAD_CONST 1 ((1, 2)) 2 UNPACK_SEQUENCE 2 4 STORE_FAST 0 (x) 6 STORE_FAST 1 (y) 8 LOAD_CONST 0 (None) 10 RETURN_VALUE Thinking some more about this, this (removing the tuples) is not a straightforward optimisation to do. Example 1: (x, y) = (y, x) is not the same as x = y ; y = x Example 2: L[v], y = a, f() is not the same as L[v] = a ; y = f() if v is a global changed by f(). I guess it's safe if the RHS is a tuple containing only constants, by which I think I mean number/string literals and built-in constants (None, True etc.). variables (NOT expressions containing variables such as "z+1") which do not occur on the LHS tuple/list/dictionary/set displays which themselves contain only the above, or nested displays which themselves ... etc. It appears (even though I use Windows where timing anything is a nightmare) that tuple versions are slightly slower. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Friday Finking: initialising values and implied tuples
On 05/04/2021 17:52, Chris Angelico wrote:
On Tue, Apr 6, 2021 at 2:32 AM Rob Cliffe via Python-list
wrote:
It doesn't appear to, at least not always. In Python 3.8.3:
from dis import dis
def f(): x = 1 ; y = 2
def g(): (x,y) = (1,2)
dis(f)
dis(g)
Output:
2 0 LOAD_CONST 1 (1)
2 STORE_FAST 0 (x)
4 LOAD_CONST 2 (2)
6 STORE_FAST 1 (y)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
3 0 LOAD_CONST 1 ((1, 2))
2 UNPACK_SEQUENCE 2
4 STORE_FAST 0 (x)
6 STORE_FAST 1 (y)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
Thinking some more about this, this (removing the tuples) is not a
straightforward optimisation to do.
It's important to be aware of the semantics here. Saying "x = 1; y =
2" requires that x be set before 2 is calculated (imagine if it had
been "y = x + 2" or something), whereas "x, y = 1, 2" has to do the
opposite, fully evaluating the right hand side before doing any of the
assignments.
I guess it's safe if the RHS is a tuple containing only
constants, by which I think I mean number/string literals and
built-in constants (None, True etc.).
variables (NOT expressions containing variables such as "z+1")
which do not occur on the LHS
tuple/list/dictionary/set displays which themselves contain only
the above, or nested displays which themselves ... etc.
Nope, there's no "it's safe if" other than constants - which are
already handled differently.
How are constants handled differently (apart from using LOAD_CONST)?
See my dis example above.
If there is ANY Python code executed to
calculate those values, it could depend on the previous assignments
being completed.
I don't understand. What semantic difference could there be between
x = { 1: 2 } ; y = [3, 4] ; z = (5, 6)
and
x, y, z = { 1:2 }, [3, 4], (5, 6)
? Why is it not safe to convert the latter to the former?
But I withdraw "set" from my "safe" list because I now realise that
"set" could be reassigned.
But the tuples aren't a problem here. They're a highly optimized way
of grabbing multiple things at once. In the "x, y = 1, 2" case, the
compiler would be free to implement it as "LOAD_CONST 1, LOAD_CONST 2,
ROT_TWO, STORE_FAST x, STORE_FAST y" (equivalent to what you'd see for
"x, y = y, x"), but it doesn't, so we can fairly confidently expect
that the tuple is faster.
Not according to the Windows timings I mentioned in my previous post.
BTW, if you want to play around with CPython's optimizations, there's
another case to consider:
def f(): x = y = 1
1 0 LOAD_CONST 1 (1)
2 DUP_TOP
4 STORE_FAST 0 (x)
6 STORE_FAST 1 (y)
The constant gets loaded once (which is semantically important), and
then duplicated on the stack, leaving two available for storing. Feel
free to play around with different combinations here. For instance,
"x, y = a, b = 1, 2" should now be unsurprising, but perhaps there'll
be some more complicated examples that are interesting to explore.
I love dis stuff. :)
ChrisA
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Friday Finking: initialising values and implied tuples
On 05/04/2021 17:52, Chris Angelico wrote:
I don't understand. What semantic difference could there be between
x = { 1: 2 } ; y = [3, 4] ; z = (5, 6)
and
x, y, z = { 1:2 }, [3, 4], (5, 6)
? Why is it not safe to convert the latter to the former?
But I withdraw "set" from my "safe" list because I now realise that
"set" could be reassigned.
Correction: set literals like {7,8} should still be OK as far as I can see.
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Friday Finking: initialising values and implied tuples
On 05/04/2021 18:33, Chris Angelico wrote: Firstly, anything with any variable at all can involve a lookup, which can trigger arbitrary code (so "variables which do not occur on the LHS" is not sufficient). Interesting. I was going to ask: How could you make a variable lookup trigger arbitrary code? Then I saw your post in the "Yield after the return in Python function" thread. (Took me a while to understand it.) So I ask: Can you make a variable lookup trigger arbitrary code, other than in code passed to eval/exec/compile? TIA Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Style qeustion: Multiple return values
On 12/04/2021 09:29, Steve Keller wrote: Just a short style question: When returning multiple return values, do you use parenthesis? E.g. would you write def foo(): return 1, 2 a, b = foo() or do you prefer def foo(): return (1, 2) (a, b) = foo() Steve My personal view: It's a matter of style; do whatever looks best to you. Looking at my own code I'm not consistent in my choice. That said: return item, cost # If I think of these as two distinct values I'd be inclined to omit the parentheses. return (x,y) # If I'm returning a point which I think of as a single entity, I'd probably put them in, # especially, for consistency, if the rest of my code contains lots of `(x,y)`s, `(u,v)`s etc. return (long-expression1, long-expression2) # It might be hard to spot the comma at first glance so the parentheses might help to recognise a tuple. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: learning python ...
On 24/05/2021 14:34, hw wrote: Your claim that I'm insulting python or anoyone is ridiculous. According to your logic, C is insulting python. I suggest you stop making assumptions. Calling a mature, widely used language "unfinished" because of what *you* see as a defect *is* insulting. (Of course, all languages evolve, and Python is no exception. But the change you are in effect proposing won't happen.) And using a hectoring arrogant tone as a self-confessed Python beginner when addressing veterans with decades of experience in *developing Python* as well as developing with Python (disclosure: I'm not describing myself) is unlikely to endear anyone to your views. Python has few keywords (deliberately so), and anything that is not a keyword can be used as an identifier. That will stop your program from crashing in 5 years time if a new built-in type is added to the language that happens to have the same name as an identifier you used (like maybe 'quaternion' 'matrix' 'group' 'query', to imagine a few). One day you may want to write (as you can in Python) class int(int): . to shadow the built-in 'int' type with a modified version. I'm not suggesting this has many real world applications, but it can be fun to play with. Python has a "consenting adults" policy: it assumes that if you do something weird, you're doing it deliberately, and it doesn't try to stop you. I am sure after a little more experience with Python you will remember the commonest built-in types (int, float, list, dict, str etc.). Regards Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: learning python ...
Please don't be put off by your experience so far. Everyone stumbles along the way and runs into "gotchas" when learning a new language. Python is a fantastic language for development. One of my early "epiphany" moments was experimenting with different algorithms to try to find the right one for a tricky task. If I remember rightly it took me about 2 weeks, on and off. I could never have done it in any flavour of C in 6 months, or at all for that matter. Python's built-in lists, dictionaries and memory management saved so much donkey work and made it so easy to throw ideas around! Regards Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Why the list creates in two different ways? Does it cause by the mutability of its elements? Where the Python document explains it?
This puzzled me, so I played around with it a bit (Python 3.8.3): n = [] for i in range(3): n.append((1,7,-3,None,"x")) for i in range(3): n.append((1,7,-3,None,"x")) print([id(x) for x in n]) a = 4 n = [] for i in range(3): n.append((1,7,-3,a,None,"x")) for i in range(3): n.append((1,7,-3,a,None,"x")) print([id(x) for x in n]) Output: [27164832, 27164832, 27164832, 27164832, 27164832, 27164832] [30065208, 30065496, 30237192, 30239976, 30240024, 30343928] Evidently the compiler is clever enough to pick out a constant tuple and create (or cause to get created) a single instance of it which is used when required. Indeed disassembling the code shows that LOAD_CONST is used to get the tuple. But it obviously can't do that when the tuple contains a variable. Rob Cliffe On 14/06/2021 20:35, Chris Angelico wrote: On Tue, Jun 15, 2021 at 5:12 AM Jach Feng wrote: n = [(1,2) for i in range(3)] n [(1, 2), (1, 2), (1, 2)] id(n[0]) == id(n[1]) == id(n[2]) True This is three tuples. Tuples are immutable and you get three references to the same thing. m = [[1,2] for i in range(3)] m [[1, 2], [1, 2], [1, 2]] id(m[0]) == id(m[1]) == id(m[2]) False These are lists. Each one is distinct. You could change one of them and the other two would remain as they are. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Strange disassembly
On 18/06/2021 11:04, Chris Angelico wrote: sys.version '3.10.0b2+ (heads/3.10:33a7a24288, Jun 9 2021, 20:47:39) [GCC 8.3.0]' def chk(x): ... if not(0 < x < 10): raise Exception ... dis.dis(chk) 2 0 LOAD_CONST 1 (0) 2 LOAD_FAST0 (x) 4 DUP_TOP 6 ROT_THREE 8 COMPARE_OP 0 (<) 10 POP_JUMP_IF_FALSE 11 (to 22) 12 LOAD_CONST 2 (10) 14 COMPARE_OP 0 (<) 16 POP_JUMP_IF_TRUE14 (to 28) 18 LOAD_GLOBAL 0 (Exception) 20 RAISE_VARARGS1 >> 22 POP_TOP 24 LOAD_GLOBAL 0 (Exception) 26 RAISE_VARARGS1 >> 28 LOAD_CONST 0 (None) 30 RETURN_VALUE Why are there two separate bytecode blocks for the "raise Exception"? I'd have thought that the double condition would still be evaluated as one thing, or at least that the jump destinations for both the early-abort and the main evaluation should be the same. ChrisA As an ornery human I could refactor this to avoid the code duplication as 2 0 LOAD_CONST 1 (0) 2 LOAD_FAST 0 (x) 4 DUP_TOP 6 ROT_THREE 8 COMPARE_OP 0 (<) 10 POP_JUMP_IF_TRUE 10 (to 18) 12 POP_TOP >> 14 LOAD_GLOBAL 0 (Exception) 16 RAISE_VARARGS 1 >> 18 LOAD_CONST 2 (10) 20 COMPARE_OP 0 (<) 22 POP_JUMP_IF_FALSE 21 (to 14) 24 LOAD_CONST 0 (None) 26 RETURN_VALUE >>> (there may be mistakes in this) but this is probably too much to expect of the compiler. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Strange disassembly
On 19/06/2021 07:50, Chris Angelico wrote: On Sat, Jun 19, 2021 at 4:16 PM Rob Cliffe via Python-list wrote: On 18/06/2021 11:04, Chris Angelico wrote: sys.version '3.10.0b2+ (heads/3.10:33a7a24288, Jun 9 2021, 20:47:39) [GCC 8.3.0]' def chk(x): ... if not(0 < x < 10): raise Exception ... dis.dis(chk) 2 0 LOAD_CONST 1 (0) 2 LOAD_FAST0 (x) 4 DUP_TOP 6 ROT_THREE 8 COMPARE_OP 0 (<) 10 POP_JUMP_IF_FALSE 11 (to 22) 12 LOAD_CONST 2 (10) 14 COMPARE_OP 0 (<) 16 POP_JUMP_IF_TRUE14 (to 28) 18 LOAD_GLOBAL 0 (Exception) 20 RAISE_VARARGS1 >> 22 POP_TOP 24 LOAD_GLOBAL 0 (Exception) 26 RAISE_VARARGS1 >> 28 LOAD_CONST 0 (None) 30 RETURN_VALUE Why are there two separate bytecode blocks for the "raise Exception"? I'd have thought that the double condition would still be evaluated as one thing, or at least that the jump destinations for both the early-abort and the main evaluation should be the same. ChrisA As an ornery human I could refactor this to avoid the code duplication as 2 0 LOAD_CONST 1 (0) 2 LOAD_FAST0 (x) 4 DUP_TOP 6 ROT_THREE 8 COMPARE_OP 0 (<) 10 POP_JUMP_IF_TRUE10 (to 18) 12 POP_TOP >> 14 LOAD_GLOBAL 0 (Exception) 16 RAISE_VARARGS1 >> 18 LOAD_CONST 2 (10) 20 COMPARE_OP 0 (<) 22 POP_JUMP_IF_FALSE 21 (to 14) 24 LOAD_CONST 0 (None) 26 RETURN_VALUE >>> (there may be mistakes in this) but this is probably too much to expect of the compiler. Hmm, I think that depends too much on knowing that the raise won't return. That might be a neat optimization (effectively a form of dead code removal), but otherwise, the best you could do would be to also have it jump out after the raise. ChrisA Er, doesn't your original dis output make the same assumption? Otherwise, after this line 20 RAISE_VARARGS1 it would attempt to do an extra pop, then raise a second time. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Anaconda navigator not working
I'm afraid I can't help at all, but I have many times (well, it feels like many) encountered the "%1 is not a valid Win32 application" error message. It looks like a format string that has not been given an argument to format. I would guess it's a bug either in Windows or somewhere in Python. Can anybody shed light on this? Best wishes Rob Cliffe PS I have a very vague idea it's to do with mixing 32-bit and 64-bit software. On 20/06/2021 05:21, Liya Ann Sunny wrote: After installing Anaconda, I tried to open the anaconda navigator but it did not work. When i check in anaconda prompt by running code anaconda it got error like this (base) C:\Users\Acer>anaconda Traceback (most recent call last): File "C:\Users\Acer\anaconda3\Scripts\anaconda-script.py", line 6, in from binstar_client.scripts.cli import main File "C:\Users\Acer\anaconda3\lib\site-packages\binstar_client\__init__.py", line 17, in from .utils import compute_hash, jencode, pv File "C:\Users\Acer\anaconda3\lib\site-packages\binstar_client\utils\__init__.py", line 17, in from .config import (get_server_api, dirs, load_token, store_token, File "C:\Users\Acer\anaconda3\lib\site-packages\binstar_client\utils\config.py", line 54, in USER_LOGDIR = dirs.user_log_dir File "C:\Users\Acer\anaconda3\lib\site-packages\binstar_client\utils\appdirs.py", line 257, in user_log_dir return user_log_dir(self.appname, self.appauthor, File "C:\Users\Acer\anaconda3\lib\site-packages\binstar_client\utils\appdirs.py", line 205, in user_log_dir path = user_data_dir(appname, appauthor, version); version = False File "C:\Users\Acer\anaconda3\lib\site-packages\binstar_client\utils\appdirs.py", line 67, in user_data_dir path = os.path.join(_get_win_folder(const), appauthor, appname) File "C:\Users\Acer\anaconda3\lib\site-packages\binstar_client\utils\appdirs.py", line 284, in _get_win_folder_with_pywin32 from win32com.shell import shellcon, shell ImportError: DLL load failed while importing shell: %1 is not a valid Win32 application. what is its solution? -- https://mail.python.org/mailman/listinfo/python-list
Re: some problems for an introductory python test
On 11/08/2021 19:10, MRAB wrote:
On 2021-08-11 18:10, Wolfram Hinderer via Python-list wrote:
Am 11.08.2021 um 05:22 schrieb Terry Reedy:
Python is a little looser about whitespace than one might expect
from reading 'normal' code when the result is unambiguous in that it
cannot really mean anything other than what it does. Two other
examples:
>>> if3: print('yes!')
yes!
>>> [0] [0]
0
Not sure what you mean here - is it a joke? The first looks like an if
statement, but isn't. The missing space *does* make a difference. (Try
"if0" instead.)
I see what you mean. It's a type annotation:
var: type
where the "type" is a print statement!
The second is normal indexing, which allows white space. I wouldn't
consider that surprising, but maybe I should? (Honest question, I really
don't know.)
I looked at the if3 example, and I was gobsmacked. I momentarily
assumed that "if3" was parsed as "if 3", although that clearly makes no
sense ("if3" is a valid identifier).
Then I saw the "if0" example and I was even more gobsmacked, because it
showed that my assumption was wrong.
I've never used type annotations, I've never planned to used them. And
now that all is revealed, I'm afraid that my reaction is: I'm even more
inclined never to use them, because these examples are (to me) so confusing.
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Problem with python
Well, up to a point.
In Python 2 the output from
print 1, 2
is '1 2'
In Python 3 if you add brackets:
print(1, 2)
the output is the same.
But if you transplant that syntax back into Python 2, the output from
print(1, 2)
is '(1, 2)'. The brackets have turned two separate items into a single
tuple.
If you want Python 2- and 3-compatibility you must find a work-around
such as
print('%d %d' % (1,2))
print('%s %s' % (1,2))
print(str(1) + ' ' + str(2))
Similarly
'print' in Python 2 outputs a blank line.
'print()' in Python 3 outputs a blank line. In python 2 it prints
a line containing a blank tuple: '()'.
A 2/3-compatible way of outputting a blank line is
print('')
Best wishes
Rob Cliffe
On 04/09/2021 20:50, Peter J. Holzer wrote:
On 2021-09-04 14:29:47 -0500, Igor Korot wrote:
Will this syntax work in python 2?
Yes. It's just a redundant pair of parentheses.
hp
--
https://mail.python.org/mailman/listinfo/python-list
Re: OT: AttributeError
Ah, Z80s (deep sigh). Those were the days! You could disassemble the entire CP/M operating system (including the BIOS), and still have many Kb to play with! Real programmers don't need gigabytes! On 29/09/2021 03:03, [email protected] wrote: On 2021-09-29 at 09:21:34 +1000, Chris Angelico wrote: On Wed, Sep 29, 2021 at 9:10 AM <[email protected]> wrote: On 2021-09-29 at 11:38:22 +1300, dn via Python-list wrote: For those of us who remember/can compute in binary, octal, hex, or decimal as-needed: Why do programmers confuse All Hallows'/Halloween for Christmas Day? That one is also very old. (Yes, I know the answer. No, I will not spoil it for those who might not.) What do I have to do to gain the insight necessary to have discovered that question and answer on my own? You'd have to be highly familiar with numbers in different notations, to the extent that you automatically read 65 and 0x41 as the same number ... I do that. And I have done that, with numbers that size, since the late 1970s (maybe the mid 1970s, for narrow definitions of "different"). There's at least one more [sideways, twisted] leap to the point that you even think of translating the names of those holidays into an arithmetic riddle. ... Or, even better, to be able to read off a hex dump and see E8 03 and instantly read it as "1,000 little-endian". 59535 big endian. Warningm flamebait ahead: Who thinks in little endian? (I was raised on 6502s and 680XX CPUs; 8080s and Z80s always did things backwards.) -- https://mail.python.org/mailman/listinfo/python-list
Re: importing a module from a file without the '.py' suffix
As far as I know, it can't be done.
If I was REALLY desperate I might try (tested)
import os
os.rename('myfile.myext', 'myfile.py')
import myfile
os.rename('myfile.py', 'myfile.myext')
# with appropriate modifications if myfile is not in the current directory
but this is a horrible solution, subject to race conditions, file
permission errors, wrong state after a crash, and probably other problems.
It could be cleaned up a bit with try ... except, but is still not to be
recommended.
Can you just create a copy of your file with a .py extension?
Best wishes
Rob Cliffe
On 22/10/2021 12:19, Antoon Pardon wrote:
I have a file with python code but the name doesn't end with the '.py'
suffix.
What is the easiest way to import this code as a module without
changing its name?
--
https://mail.python.org/mailman/listinfo/python-list
Re: [tkinter][Windows]Unexpected state report for the tab key
On 25/10/2021 02:57, Stefan Ram wrote: GetKeyState still returns that the tab key is pressed after the tab key already has been released. Well, how then am I going to make my slide show stop the moment the key is being released? Does tkinter allow you to trap KeyUp (and KeyDown) events? (I'm not familiar with tkinter, but in wxpython you can.) And if so, does trapping KeyUp solve the problem? Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Unexpected behaviour of math.floor, round and int functions (rounding)
On 20/11/2021 22:59, Avi Gross via Python-list wrote: there are grey lines along the way where some mathematical proofs do weird things like IGNORE parts of a calculation by suggesting they are going to zero much faster than other parts and then wave a mathematical wand about what happens when they approach a limit like zero and voila, we just "proved" that the derivative of X**2 is 2*X or the more general derivative of A*(X**N) is N*A*(X**(N-1)) and then extend that to N being negative or fractional or a transcendental number and beyond. You seem to be maligning mathematicians. What you say was true in the time of Newton, Leibniz and Bishop Berkeley, but analysis was made completely rigorous by the efforts of Weierstrass and others. There are no "grey lines". Proofs do not "suggest", they PROVE (else they are not proofs, they are plain wrong). It is not the fault of mathematicians (or mathematics) if some people produce sloppy hand-wavy "proofs" as justification for their conclusions. I am absolutely sure you know all this, but your post does not read as if you do. And it could give a mistaken impression to a non-mathematician. I think we have had enough denigration of experts. Best Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Unexpected behaviour of math.floor, round and int functions (rounding)
On 21/11/2021 01:02, Chris Angelico wrote: If you have a number with a finite binary representation, you can guarantee that it can be represented finitely in decimal too. Infinitely repeating expansions come from denominators that are coprime with the numeric base. Not quite, e.g. 1/14 is a repeating decimal but 14 and 10 are not coprime. I believe it is correct to say that infinitely recurring expansions occur when the denominator is divisible by a prime that does not divide the base. Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Negative subscripts
You could evaluate y separately: yval = for item in x[:-yval] if yval else x: [do stuff] or you could do it using the walrus operator: for item in x[:-yval] if (yval := ) else x: [do stuff] or, perhaps simplest, you could do for item in x[:-y or None]: # a value of None for a slice argument means "don't slice here" [do stuff] Best wishes Rob Cliffe On 26/11/2021 09:17, Frank Millman wrote: Hi all In my program I have a for-loop like this - >>> for item in x[:-y]: ... [do stuff] 'y' may or may not be 0. If it is 0 I want to process the entire list 'x', but of course -0 equals 0, so it returns an empty list. In theory I can say >>> for item in x[:-y] if y else x: ... [do stuff] But in my actual program, both x and y are fairly long expressions, so the result is pretty ugly. Are there any other techniques anyone can suggest, or is the only alternative to use if...then...else to cater for y = 0? Thanks Frank Millman -- https://mail.python.org/mailman/listinfo/python-list
Re: A decade or so of Python programming, and I've never thought to "for-elif"
If for ... else was spelt more intelligibly, e.g. for ... nobreak, there
would be no temptation to use anything like `elif'. `nobreakif' wouldn't
be a keyword.
Rob Cliffe
On 30/11/2021 06:24, Chris Angelico wrote:
for ns in namespaces:
if name in ns:
print("Found!")
break
elif name.isupper():
print("All-caps name that wasn't found")
This actually doesn't work. I have been programming in Python for well
over a decade, and never before been in a situation where this would
be useful.
As YAGNIs go, this is right up there.
(For the record, since this was the last thing in the function, I just
made the break a return. Alternatively, an extra indentation level
"else: if name.isupper():" wouldn't have been that terrible.)
Your random piece of amusement for today.
ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: Global VS Local Subroutines
On 10/02/2022 12:13, BlindAnagram wrote: Is there any difference in performance between these two program layouts: def a(): ... def(b): c = a(b) or def(b): def a(): ... c = a(b) I would appreciate any insights on which layout to choose in which circumstances. The way to answer questions about performance is not to guess (often difficult or impossible), but to measure. Profiling tools are available to see where a program spends how much of its time. But a simpler approach might be to try to run the guts of your program 1000 or 100 times, and find how long it takes with each layout (not with a stopwatch 😁 but getting the program to record the start time, end time and the difference). That said, I will venture a guess (or at least, an observation): def a(): ... is executable code. It creates the function `a` which did not exist before (or creates the new version if it did). This takes a certain amount of time. In your 2nd layout, this overhead occurs every time b() is called. So I would *guess* that this would be slower. Of course it depends on how many times b() is called and probably on lots of other things. But of course, performance is not the only consideration, as per Chris Angelico's answer. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Global VS Local Subroutines
On 10/02/2022 21:43, Friedrich Rentsch wrote: I believe to have observed a difference which also might be worth noting: the imbedded function a() (second example) has access to all of the imbedding function's variables, which might be an efficiency factor with lots of variables. The access is read-only, though. If the inner function writes to one of the readable external variables, that variable becomes local to the inner function. You can make it continue to refer to the variables of the imbedding function, i.e. b(), by declaring them non-local, e.g. nonlocal c Rob Cliffe Frederic On 2/10/22 1:13 PM, BlindAnagram wrote: Is there any difference in performance between these two program layouts: def a(): ... def(b): c = a(b) or def(b): def a(): ... c = a(b) I would appreciate any insights on which layout to choose in which circumstances. -- https://mail.python.org/mailman/listinfo/python-list
Re: All permutations from 2 lists
I would not use `os` as an identifier, as it is the name of an important
built-in module.
I think itertools.product is what you need.
Example program:
import itertools
opsys = ["Linux","Windows"]
region = ["us-east-1", "us-east-2"]
print(list(itertools.product(opsys, region)))
Output:
[('Linux', 'us-east-1'), ('Linux', 'us-east-2'), ('Windows',
'us-east-1'), ('Windows', 'us-east-2')]
itertools.product returns an iterator (or iterable, I'm not sure of the
correct technical term).
If you only want to use the result once you can write e.g.
for ops, reg in itertools.product(opsys, region):
etc.
If you need it more than once, you can convert it to a list (or tuple),
as above.
Best wishes
Rob Cliffe
On 02/03/2022 00:12, Larry Martell wrote:
If I have 2 lists, e.g.:
os = ["Linux","Windows"]
region = ["us-east-1", "us-east-2"]
How can I get a list of tuples with all possible permutations?
So for this example I'd want:
[("Linux", "us-east-1"), ("Linux", "us-east-2"), ("Windows",
"us-east-1"), "Windows", "us-east-2')]
The lists can be different lengths or can be 0 length. Tried a few
different things with itertools but have not got just what I need.
TIA!
--
https://mail.python.org/mailman/listinfo/python-list
Re: All permutations from 2 lists
On 03/03/2022 14:07, Larry Martell wrote:
On Wed, Mar 2, 2022 at 9:42 PM Avi Gross via Python-list
wrote:
Larry,
i waited patiently to see what others will write and perhaps see if you explain
better what you need. You seem to gleefully swat down anything offered. So I am
not tempted to engage.
But then you gave in to the temptation.
And it is hard to guess as it is not clear what you will do with this.
In the interests of presenting a minimal example I clearly
oversimplified. This is my use case: I get a dict from an outside
source. The dict contains key/value pairs that I need to use to query
a mongodb database. When the values in the dict are all scalar I can
pass the dict directly into the query, e.g.:
self._db_conn[collection_name].find(query). But if any of the values
are lists that does not work. I need to query with something like the
cross product of all the lists. It's not a true product since if a
list is empty it means no filtering on that field, not no filtering on
all the fields. Originally I did not know I could generate a single
query that did that. So I was trying to come up with a way to generate
a list of all the permutations and was going to issue a query for each
individually. Clearly that would become very inefficient if the lists
were long or there were a lot of lists. I then found that I could
specify a list with the "$in" clause, hence my solution.
def query_lfixer(query):
for k, v in query.items():
if type(v)==list:
query[k] = {"$in": v}
return query
self._db_conn[collection_name].find(query_lfixer(query))
So why did so many of us bother?
Indeed - so why did you bother?
You started with a request for help that did not say what you actually
wanted.
When people took the time and trouble to give you answers to the
question **as posed**, you repeatedly rejected them while still being
unclear about what you actually wanted.
Now you seem to be being rude to Avi.
There are plenty of people willing to help on this list, and plenty of
goodwill to those that need help - but goodwill can be used up if it is
abused.
Respectfully,
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Behavior of the for-else construct
It has occasional uses (I THINK I've used it myself) but spelling it
`else` is very confusing. So there have been proposals for an
alternative spelling, e.g. `nobreak`.
There have also been suggestions for adding other suites after `for', e.g.
if the loop WAS exited with `break`
if the loop was executed zero times
but these have not been accepted.
Best wishes
Rob Cliffe
On 03/03/2022 13:24, computermaster360 wrote:
I want to make a little survey here.
Do you find the for-else construct useful? Have you used it in
practice? Do you even know how it works, or that there is such a thing
in Python?
I have used it maybe once. My issue with this construct is that
calling the second block `else` doesn't make sense; a much more
sensible name would be `then`.
Now, imagine a parallel universe, where the for-else construct would
have a different behavior:
for elem in iterable:
process(elem)
else:
# executed only when the iterable was initially empty
print('Nothing to process')
Wouldn't this be more natural? I think so. Also, I face this case much
more often than having detect whether I broke out of a loop early
(which is what the current for-else construct is for).
Now someone may argue that it's easy to check whether the iterable is
empty beforehand. But is it really? What if it's an iterator?
Then one would have to resort to using a flag variable and set it in
each iteration of the loop. An ugly alternative would be trying to
retrieve
the first element of the iterable separately, in a try block before
the for-loop, to find out whether the iterable is empty. This would of
course
require making an iterator of the iterable first (since we can't be
sure it is already an iterator), and then -- if there are any elements
-- processing
the first element separately before the for-loop, which means
duplicating the loop body. You can see the whole thing gets really
ugly really quickly...
What are your thoughts? Do you agree? Or am I just not Dutch enough...?
--
https://mail.python.org/mailman/listinfo/python-list
Re: Behavior of the for-else construct
I find it so hard to remember what `for ... else` means that on the very few occasions I have used it, I ALWAYS put a comment alongside/below the `else` to remind myself (and anyone else unfortunate enough to read my code) what triggers it, e.g. for item in search_list: ... ... break else: # if no item in search_list matched the criteria You get the idea. If I really want to remember what this construct means, I remind myself that `else` here really means `no break`. Would have been better if it had been spelt `nobreak` or similar in the first place. Rob Cliffe On 03/03/2022 23:07, Avi Gross via Python-list wrote: The drumbeat I keep hearing is that some people hear/see the same word as implying something else. ELSE is ambiguous in the context it is used. And naturally, since nobody desperately wants to use non-reserved keywords, nobody seems ready to use a word like INSTEAD instead. Ideally, a language should be extendable and some languages like R allow you to place all kinds of things inside percent signs to make new operators like %*% or %PIPE% ... Just because some feature may be wanted is not a good reason to overly complicate a language. Can you imagine how hard it would be both to implement and read something like: ... ELSE: ... OK: ... FINALLY: ... ULTIMATELY: ... What if multiple of things like the above example need to be triggered in some particular order? I have to wonder if some new form of wrapper might have made as much sense as in you wrap your loop in something that sets up and traps various signals that are then produced under conditions specified such as the loop not being entered as the starting condition is sort of null, or an exit due to a break or simply because the code itself throws that signal to be caught ... This reminds me a bit of how some programs add so much functionality because someone thought of it without wondering if anyone (including the ones who sponsored it) would ever want to use it or remember it is there or how. I recall how a version of emacs had a transpose-letter function so after typing "teh" you could hit control-t and a little mock LISP macro would go back and co a cut and go forward and do a paste and leave the cursor where it was. That was sometimes useful, but often just as easy to backspace and retype. But I recall gleefully adding a transpose for words, sentences, paragraphs and was going to add more but I was running out of keystrokes to bind them to and besides it can be fairly easy to select items and yank them and move to where you want them and replace them. To make changes in a language is sometimes really expensive but also dangerous. A "free" language must be added to sparingly and with so many requests, perhaps only a few non bug-fixes can seriously be considered. -Original Message- From: Akkana Peck To: [email protected] Sent: Thu, Mar 3, 2022 5:33 pm Subject: Re: Behavior of the for-else construct computermaster360 writes: I want to make a little survey here. Do you find the for-else construct useful? No. Have you used it in practice? Once or twice, but ended up removing it, see below. Do you even know how it works, or that there is such a thing in Python? I always have to look it up, because to my mind, "else" implies it does something quite different from what it actually does. Which means that even if I worked hard at memorizing what it does, so I didn't have to look it up, I still wouldn't use it in code, because I want my code to be easily readable (including by future-me). for..else makes code difficult to understand by anyone who doesn't use for..else frequently: they might be misled into misunderstanding the control flow. ...Akkana -- https://mail.python.org/mailman/listinfo/python-list
Re: Behavior of the for-else construct
On 04/03/2022 00:34, Chris Angelico wrote: On Fri, 4 Mar 2022 at 10:09, Avi Gross via Python-list wrote: The drumbeat I keep hearing is that some people hear/see the same word as implying something else. ELSE is ambiguous in the context it is used. What I'm hearing is that there are, broadly speaking, two types of programmers [1]: 1) Those who think about "for-else" as a search tool and perfectly understand how it behaves 2) Those who have an incorrect idea about what for-else is supposed to do, don't understand it, and don't like it. Or those who have a vague fuzzy idea about it and have trouble remembering what it does. You could easily make a similar point about a lot of other advanced constructs. Some people don't understand threading, and either dislike it or are scared of it. Some people never get their heads around asyncio and the way that yield points work. Some people can't grok operator precedence, so they parenthesize everything "just to be safe". And some people dislike exceptions so much that they warp all their functions into returning a (value,True) or (error,False) tuple instead. Does this mean that all these features are bad? No. You could add examples ad nauseam. I submit that for-else is a special case. As evidenced by the number of people (including me) who say they have trouble grokking it. There's no way to make every feature perfectly intuitive to every programmer. Those features are still incredibly useful to the programmers that DO use them. Maybe, with hindsight, for-finally would have been a slightly better spelling than for-else. No. "finally" suggests (as analogy to try...finally) that the "finally" suit body is always executed. Presumably even if an untrapped exception occurs in the for-loop body (or even in the for-loop iterator). A for-loop can be terminated with "break" for many conceptually different reasons e.g. A search for a suitable item has found one. Something unexpected has happened. A pre-set allowed execution time has been exceeded. "nobreak"/"no_break" etc. is explicit and conceptually neutral. Who knows. But people simply need to understand it, just like people need to understand how binary floating-point works, and claiming that it's "ambiguous' is simply wrong. It has one meaning in the language, and then if programmers have an incorrect expectation, they need to learn (or to not use the feature, which isn't really a problem, it's just not taking advantage of it). And naturally, since nobody desperately wants to use non-reserved keywords, nobody seems ready to use a word like INSTEAD instead. Ideally, a language should be extendable and some languages like R allow you to place all kinds of things inside percent signs to make new operators like %*% or %PIPE% ... I don't know what you mean by "extendable", but if you mean that different people should be able to change the language syntax in different ways, then absolutely not. When two different files can be completely different languages based on a few directives, it's extremely difficult to read. +0.9, although I do sometimes wish for a macro feature in Python. Like, say, one that would translate "nobreak" into "else". 😁 (Import hooks, and tools like MacroPy, can be used for this sort of effect. I haven't tried MacroPy yet, maybe someday I will. I do not think that we should be using them on a regular basis to change core syntax.) Just because some feature may be wanted is not a good reason to overly complicate a language. Can you imagine how hard it would be both to implement and read something like: ... ELSE: ... OK: ... FINALLY: ... ULTIMATELY: ... What if multiple of things like the above example need to be triggered in some particular order? It would be easy to read if they were spelt sensibly, e.g. if_no_iterations: if_one_iteration: if_multiple_iterations: if_any_iterations: if_break: if_no_break: (I'm not saying that all of these are desirable, just conveying the idea.) If multiple clauses were triggered, they should be executed in the order in which they occur in the code. I don't know what they'd all mean, but if they were all in the core language, they would have to be supported in arbitrary combinations. It's possible to have a "try-except-else-finally" block in Python, for instance. But if you mean that they should all do what "else" does now, then this is a terrible idea. One way of spelling it is just fine. This reminds me a bit of how some programs add so much functionality because someone thought of it without wondering if anyone (including the ones who sponsored it) would ever want to use it or remember it is there or how. I recall how a version of emacs had a transpose-letter function so after typing "teh" you could hit control-t and a little mock LISP macro would go back and co a cut and go forward and do a paste and leave the cursor where it was. That was sometimes useful, bu
Re: Behavior of the for-else construct
On 04/03/2022 00:38, Avi Gross via Python-list wrote:
Rob,
I regularly code with lots of comments like the one you describe, or mark the
end of a region that started on an earlier screen such as a deeply nested
construct.
So do I (and not just in Python). It's good practice.
I have had problems though when I have shared such code and the recipient
strips my comments and then later wants me to make changes or even just explain
it! My reply tends to be unprintable as in, well, never mind!
Quite justified. But why not make changes to/explain from *your*
version, not his?
This leads to a question I constantly ask. If you were free to design a brand
new language now, what would you do different that existing languages have had
to deal with the hard way?
That's such a big question that I can't give an adequate answer.
I recall when filenames and extensions had a limited number of characters allowed and
embedded spaces were verboten. This regularity made lots of code possible but then some
bright people insisted on allowing spaces and you can no longer easily do things like
expand *.c into a long line of text and then unambiguously work on one file name at a
time. You can often now create a name like "was file1.c and now is file2.c" and
it seems acceptable. Yes, you can work around things and get a vector or list of strings
and not a command line of text and all things considered, people can get as much or more
work done.
I have seen major struggles to get other character sets into languages. Any new
language typically should have this built in from scratch and should consider
adding non-ASCII characters into the mix. Mathematicians often use lots of weird
braces/brackets as an example while normal programs are limited to [{( and maybe
< and their counterparts. This leads to odd Python behavior (other languages
too) where symbols are re-used ad nauseam. { can mean set or dictionary or simply
some other way to group code.
So I would love to see some key that allows you to do something like L* to mean the combination is
a left bracket and should be treated as the beginning of a sequence expected to end in R* or
perhaps *R. That would allow many other symbols to be viewed as balanced entities. Think of how
Python expanded using single and double quotes (which arguably might work better if balanced this
way) to sometimes using triple quotes to putting letters like "b" or "f" in
front to make it a special kind of string.
But I suspect programming might just get harder for those who would not
appreciate a language that used (many) hundreds of symbols.
+1. Just remembering how to type them all would be a burden.
I do work in many alphabets and many of them pronounce and use letters that
look familiar in very different ways and sound them differently and invent new
ones. Every time I learn another human language, I have to both incorporate the
new symbols and rules and also segregate them a bit from identical or similar
things in the languages I already speak. It can be quite a chore. But still, I
suspect many people are already familiar with symbols such as from set Theory
such as subset and superset that could be used as another pair of parentheses
of some type Having a way to enter them using keyboards is a challenge.
Back to the topic, I was thinking wickedly of a way to extend the FOR loop with
existing keywords while sounding a tad ominous is not with an ELSE but a FOR
... OR ELSE ...
-Original Message-
From: Rob Cliffe via Python-list
To: [email protected]
Sent: Thu, Mar 3, 2022 7:13 pm
Subject: Re: Behavior of the for-else construct
I find it so hard to remember what `for ... else` means that on the very
few occasions I have used it, I ALWAYS put a comment alongside/below the
`else` to remind myself (and anyone else unfortunate enough to read my
code) what triggers it, e.g.
for item in search_list:
...
... break
else: # if no item in search_list matched the criteria
You get the idea.
If I really want to remember what this construct means, I remind myself
that `else` here really means `no break`. Would have been better if it
had been spelt `nobreak` or similar in the first place.
Rob Cliffe
On 03/03/2022 23:07, Avi Gross via Python-list wrote:
The drumbeat I keep hearing is that some people hear/see the same word as
implying something else. ELSE is ambiguous in the context it is used.
And naturally, since nobody desperately wants to use non-reserved keywords,
nobody seems ready to use a word like INSTEAD instead.
Ideally, a language should be extendable and some languages like R allow you to
place all kinds of things inside percent signs to make new operators like %*%
or %PIPE% ...
Just because some feature may be wanted is not a good reason to overly
complicate a language. Can you imagine how hard it would be both to implement
Re: Behavior of the for-else construct
On 04/03/2022 00:43, Chris Angelico wrote:
On Fri, 4 Mar 2022 at 11:14, Rob Cliffe via Python-list
wrote:
I find it so hard to remember what `for ... else` means that on the very
few occasions I have used it, I ALWAYS put a comment alongside/below the
`else` to remind myself (and anyone else unfortunate enough to read my
code) what triggers it, e.g.
for item in search_list:
...
... break
else: # if no item in search_list matched the criteria
A "break" statement always takes you to the line following the current
loop construct. The "else" block is part of the current loop
construct. It's no more a "no-break" block than the body of the for
loop is a "no-break" body here:
for item in stuff:
if condition: break
frobnicate(item) # if no previous item matched the condition
You get the idea.
If I really want to remember what this construct means, I remind myself
that `else` here really means `no break`. Would have been better if it
had been spelt `nobreak` or similar in the first place.
Maybe, but I think that obscures the meaning of it; "finally" isn't
quite right either (in a try block, you'd hit a finally block whether
you raise an exception or not), but I think it's closer. Creating a
new language keyword is an incredibly high cost.
Think of it like this:
for item in search_list:
if condition: pass
else:
print("Condition not true for this item")
for item in search_list:
if condition: break
else:
print("Condition not true for any item")
There's a parallel here. Since a for-else loop is basically useless
without an if-break construct inside it,
Yes but you have to remember what for-else means even to grasp that point.
the else clause can be
thought of as the else on a massive if/elif chain:
if stuff[0].is_good:
print("This item is good", stuff[0])
elif stuff[1].is_good:
print("This item is good", stuff[1])
...
...
elif stuff[n].is_good:
print("This item is good", stuff[n])
else:
print("All items are bad")
As a loop, this looks like this:
for item in stuff:
if item.is_good:
print("This item is good", item)
break
else:
print("All items are bad")
The else is attached to the for so that it compasses ALL the if
statements, but it's still broadly saying "do this when we don't hit
the 'if' condition".
Whether that's a sufficient mnemonic, I don't know,
Not for me, I'm afraid.
but it doesn't
really matter; the construct is useful to those of us who want it, and
if other people ignore it, that's fine. Nobody ever said you had to
use or understand every single feature of the language you're using.
ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: Behavior of the for-else construct
On 04/03/2022 01:44, Ethan Furman wrote: On 3/3/22 5:32 PM, Rob Cliffe via Python-list wrote: > There are three types of programmer: those that can count, and those that can't. Actually, there are 10 types of programmer: those that can count in binary, and those that can't. 1, 10, many. No problem. -- ~Ethan~ -- https://mail.python.org/mailman/listinfo/python-list
Re: Behavior of the for-else construct
On 04/03/2022 00:55, Chris Angelico wrote: for victim in debtors: if victim.pay(up): continue if victim.late(): break or else: victim.sleep_with(fishes) If you mean "or else" to be synonymous with "else", then only the last debtor is killed, whether he has paid up or not, which seems a tad unfair (except that if there are no debtors either you will crash with a NameError or some random victim will be killed, which seems consistent with Mafia modus operandi while also being a trifle unfair. If (as I suspect) you mean "or else" to mean 'if a break occurred', then at least only one debtor is killed, as an example to the others, and no Exception will occur in the unlikely event of "debtors" being empty. Happy fund-raising! Rob Cliffe There's something in this. ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Behavior of the for-else construct
On 04/03/2022 20:52, Avi Gross via Python-list wrote:
I have an observation about exception handling in general. Some people use
exceptions, including ones they create and throw, for a similar idea. You might
for example try to use an exception if your first attempt fails that specifies
trying the next attempt. In my example of the PATH variable, you might use
something like calling a function and giving it what you are looking for as
well as a local copy of the PATH variable content and the exception would be to
call the function again with the first component of PATH removed until you fail
with an empty PATH. Yes, this is similar to just having a recursive function.
That sounds neither readable nor efficient compared with using split()
plus a loop. Maybe you mean this to be a toy, unrealistic example?
So the example tossed at us looks a bit more like this and it does run the ELSE
not because the loop is not done but because the loop never calls a break:
for x in range(0):
print(x)
else:
print("Finally finished!")
This would be more readable with a `zeroiterations` keyword, which
accurately describes both the semantics and the intent.
Which leads me right back to wondering why the sentinel approach is so bad!
It's not that bad, but it's more convenient and readable if it can be
avoided.
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Behavior of the for-else construct
On 05/03/2022 01:15, Cameron Simpson wrote: I sort of wish it had both "used break" and "did not use break" branches, a bit like try/except/else. And "zero iterations". Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Program chaining on Windows
On WIndows 10, running Python programs in a DOS box, I would like one
Python program to chain to another. I.e. the first program to be
replaced by the second (*not* waiting for the second to finish, as with
e.g. os.system). This doesn't seem a lot to ask, but so far I have been
unable to so this. I am using Python 3.8.3. Some attempts so far (may
be nonsensical):
ATTEMPT #1
# File X1.py
import os
print("This is X1")
os.execl('C:\\Python38\\python.exe', 'X2.py')
# File X2.py
print("This is X2")
When I type "X1.py", it prints "This is X1", then starts the Python
interactive interpreter. Furthermore:
TLDR: Weird behaviour
Long version: If I attempt to exit the interpreter with ctl-Z,
this apparently succeeds (displaying the Windows command prompt), but in
reality it is still in the interpreter, e.g. if I type "dir" it responds
"" followed by the ">>>" interpreter prompt. And
this cycle (ctl-Z etc.) can be repeated ad nauseam. If instead I try to
exit from the interpreter with "exit()", the cursor moves to the next
line and the interpreter waits for more input (but without displaying
the prompt). If I try "exit()" again, the whole DOS box disappears.
ATTEMPT #2
-
# File X1.py
import os
print("This is X1")
os.execl("C:\\Python38\\python.exe X2.py", '')
This raises ValueError: execv() arg 2 first element cannot be empty
ATTEMPT #3
import os, sys
print("This is X1")
os.execl("%s X2.py" % sys.executable, "X2.py")
This raises FileNotFoundError: [Errno 2] No such file or directory
ATTEMPT #4
# File X1.py
import os, sys
print("This is X1")
os.execv(sys.executable, ['X2.py'])
This behaves the same as, or similarly to, Attempt #1.
ATTEMPT #5
# File X1.py
import os
print("This is X1")
os.popen('python X2.py')
# File X2.py as previously
TLDR: Really weird behaviour!
Long version: If I type "X1.py", it displays "This is X1" followed
by the DOS prompt. If I type in a DOS command, it is executed, and the
DOS prompt displayed. However, if I type in another DOS command,
nothing happens except that the cursor moves to the next line and waits
for input (no prompt). If I type in a further DOS command, it is
executed. If I type still another DOS command, I see
Exception ignored in: encoding='cp1252'>
OSError: [Errno 22] Invalid argument
and the cursor moves to the next line (no prompt). If I type in one
more DOS command, it is executed, and we appear to be back to normal DOS
operation.
ATTEMPT #6
-
# File X1.py
import subprocess, sys
print("This is X1")
subprocess.Popen('X2.py', executable=sys.executable)
This behaves the same as, or similarly to, Attempt #1.
ATTEMPT #7
-
# File X1.py
import subprocess, sys
print("This is X1")
subprocess.Popen('-c X2.py', executable=sys.executable) # added -c
# File X2.py
print("This is X2")
Some progress (maybe). This prints "This is X1", then the DOS prompt
followed by "This is X2", then the cursor moves to the next line and
sits waiting for input (no prompt). If I then type something in, this
is interpreted as a DOS command, and finally the DOS prompt is
displayed. To find out more about what is happening:
ATTEMPT #8
# File X1.py as above
# File X2.py
print("This is X2")
input("Press Enter to continue X2")
input("Press Enter to quit X2")
If I type "X1.py", it displays:
This is X1
C:\Python38>This is X2
Press Enter to continue X2
Now:
TLDR: More weird behaviour, as if Windows and X2.py were taking
turns to collect lines from the console.
Long version: Now if I type something in and press Enter, it is
interpreted as a *DOS command". Then the DOS prompt is displayed. Now
if I (type something and) hit Enter, I see
Press Enter to quit X2
Now if I type something and hit Enter, it is interpreted as a DOS
command, and the DOS prompt is displayed again. Now if I type in a DOS
command and press Enter, it is ignored but the cursor moves to the next
line and waits for input (no prompt). Now if I type another DOS
command, it is executed. Finally we appear to be done (the DOS prompt
is displayed and we are back to normal DOS operation).
Am I missing something? Is there a way in Windows for one Python
program to "chain" to another (or indeed to any executable) without
waiting for the latter to finish?
Thanks in advance
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Program chaining on Windows
Thanks for everyone who replied so far, it is appreciated. (I don't
particularly like asking for help and taking up other peoples' time, but
I really ran out of ideas.)
Chris, thanks for your explanation:
With exec, the intention is to*replace* the current program, not to
invoke it and wait till it's done. The current program will not
continue after a successful exec.
That describes exactly what I want to do, but haven't succeeded in doing
so far. It's a bit frustrating that I can't do what I have been doing
routinely for decades in another language.
dn asked
What is the definition of "finish" in the first program?
Not sure if have understood <<<(*not* waiting for the second to
finish, as with e.g. os.system)>>>.
My definition of "finish" is that the program exits (sys.exit() and
friends), without waiting for the second program to finish.
So if I were "chaining" say to a .exe file, the Python interpreter would
shut down immediately.
In Chris' words, I want the second program to *replace* the first one.
Barry, thanks for your suggestion:
os.execl('C:\\Python38\\python.exe', 'C:\\Python38\\python.exe',
'X2.py')
but I'm afraid it didn't help. I tried it and got the same behaviour
(Python and Windows alternately grabbing console input lines) as my
"ATTEMPT #8" which used
subprocess.Popen('-c X2.py', executable=sys.executable)
Anything dubious about exec (or whatever) doesn't worry me as this is
development, not live installation.
Let me describe my actual use case. I am developing a large Python
program (in Windows, working in DOS boxes) and I constantly want to
modify it and re-run it. What I have been doing is to make it respond
to a hotkey by calling itself via os.system. The problem is that if I
do this 50 times I am left with 51 instances still running, each one
waiting for the next to finish. That's actually OK, except that when I
finally shut it down, it takes a long time (about 2.5 sec per instance).
I have found a workaround: a small shell program which calls the main
program (wth os.system), then when the latter exits, calls it again (if
required). Starting the main program is slightly slower, but acceptably
so; shutting it down becomes constant time.
But I would still like to be able to do it as I originally planned, if
possible. Not least because I may have other uses for program
"chaining" in future.
Best wishes
Rob Cliffe
On 23/08/2020 21:37, dn via Python-list wrote:
On 23/08/2020 19:31, Rob Cliffe via Python-list wrote:
On WIndows 10, running Python programs in a DOS box, I would like one
Python program to chain to another. I.e. the first program to be
replaced by the second (*not* waiting for the second to finish, as
with e.g. os.system). This doesn't seem a lot to ask, but so far I
have been unable to so this. I am using Python 3.8.3. Some attempts
so far (may be nonsensical):
What is the definition of "finish" in the first program?
Not sure if have understood <<<(*not* waiting for the second to
finish, as with e.g. os.system)>>>.
In Python, the easiest way to chain two "modules" is to use import.
This gives full control to the 'import-er'.
ATTEMPT #1
# File X1.py
import os
print("This is X1")
os.execl('C:\\Python38\\python.exe', 'X2.py')
# File X2.py
print("This is X2")
# File X1.py
import os
def fn():
print("This is X1")
os.execl('C:\\Python38\\python.exe', 'X2.py')
# !
# File X2.py
def fn():
print("This is X2")
# File x3.py
import x1
import x2
x1.fn()
x2.fn()
print( "x3 terminating" )
--
https://mail.python.org/mailman/listinfo/python-list
Re: Program chaining on Windows
On 24/08/2020 00:08, Eryk Sun wrote:
For Windows, we need to spawn, wait, and proxy the exit status.
Sorry, I understand very little of this. Here's where I get stuck:
(1) "*spawn*": I see that there are some os.spawn* functions. I
tried this:
# File X1.py
import os, sys
print("This is X1")
pid = os.spawnl(os.P_NOWAIT, "C:\\Python38\\python.exe",
"C:\\Python38\\X2.py")
print(f"X1 got {pid=}")
sys.exit()
but when I ran it, it displayed
This is X1
X1 got pid=...
and then apparently started the Python interactive interpreter, although
in reality things were in one of the confused states that I described in
my first post.
(FWIW If I type
"C:\\Python38\\python.exe" "C:\\Python38\\X2.py"
from the DOS prompt, it works as expected.)
(2) "*wait*": Wait for what? How?
(3) "*proxy the exit status*": Sorry, I have no idea what this means.
Are you suggesting something I could do in Python that would achieve my
aim of *replacing* one program by another (or merely e.g. describing
something you could do in C)?
If so, is there any chance you could give me some toy code?
As you can see, I'm pretty well lost.
Thanks again
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
Re: Program chaining on Windows
On 24/08/2020 00:57, Chris Angelico wrote: On Mon, Aug 24, 2020 at 9:51 AM Rob Cliffe via Python-list wrote: Let me describe my actual use case. I am developing a large Python program (in Windows, working in DOS boxes) and I constantly want to modify it and re-run it. What I have been doing is to make it respond to a hotkey by calling itself via os.system. The problem is that if I do this 50 times I am left with 51 instances still running, each one waiting for the next to finish. That's actually OK, except that when I finally shut it down, it takes a long time (about 2.5 sec per instance). I have found a workaround: a small shell program which calls the main program (wth os.system), then when the latter exits, calls it again (if required). Starting the main program is slightly slower, but acceptably so; shutting it down becomes constant time. But I would still like to be able to do it as I originally planned, if possible. Not least because I may have other uses for program "chaining" in future. Hmm. Python isn't really set up to make this sort of thing easy. I guess this sentence pretty well answers my whole post. :-( My recommendations, in order of priority, would be: 1) What you're doing, but with a dedicated exit code that means "changed, please restart me" That is pretty much what I am now doing. I return (to a shell program) exit codes which mean "such-and-such a hotkey was hit, please restart me". 2) The same thing but entirely within Python. Instead of importing modules, manually load them and exec the source code. (No relation to the process-level exec - I'm talking about the exec function.) Updating is way WAY faster, but it isn't the same thing as restarting. Your code has to be built with this in mind. Ah, there's the rub. This program will be run stand-alone at client sites and I need to test it "as is". As I see it I can't responsibly rewrite the way it works (if I'm understanding you more or less correctly). 3) Similar to #2 but using a language that's designed with that kind of thing in mind. Off topic for this list but it's something I've done on a number of occasions, so I'd be happy to discuss that with you. No, no, no, Python is for me! (Seriously: rewriting in another language would be far too time-consuming and *error-prone*. Not to mention unenjoyable.) 4) Any other option available 5) Messing with exec on Windows Which I've been doing, but without success. Thanks again, Chris. Rob ChrisA -- https://mail.python.org/mailman/listinfo/python-list
Re: Program chaining on Windows
On 24/08/2020 13:20, Eryk Sun wrote: You can work with job objects via ctypes or PyWin32's win32job module. I can provide example code for either approach. No need, I'm already convinced that this is not the way for me to go. from the DOS prompt, it works as expected. You're not running DOS. Most likely the shell you're running is CMD or PowerShell, attached to a console session that's hosted by either conhost.exe (default) or openconsole.exe (an open-source build of conhost.exe that's used by the new Windows Terminal). These are Windows applications. I'm running CMD.exe. Sorry, I didn't realise I was using sloppy language. Thanks for your very detailed repy, Eryk. Best wishes Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: Program chaining on Windows
On 24/08/2020 19:54, Terry Reedy wrote: On 8/23/2020 3:31 AM, Rob Cliffe via Python-list wrote: On WIndows 10, running Python programs in a DOS box, Please don't use 'DOS box' for Windows Command Prompt or other Windows consoles for running Windows programs from a command line. DOSBox is program for running (old) DOS programs written for the IBM PC. One use is running old games, such as some distributed by GOG. Note that interactive command line interpreters existed long before MS/PC DOS. Sorry, I was unintentionally using misleading terminology. I was running programs in a cmd.exe window. I would like one Python program to chain to another. I.e. the first program to be replaced by the second (*not* waiting for the second to finish, as with e.g. os.system). For reasons explained especially well by Eryk Sun, you should probably use one python program as a master program to run others in succession. Yes, thats's what I'm doing now. Thanks Rob Cliffe -- https://mail.python.org/mailman/listinfo/python-list
Re: About float/double type number in range.
On 26/08/2020 12:02, Peter J. Holzer wrote: On 2020-08-26 22:40:36 +1200, dn via Python-list wrote: On 26/08/2020 19:58, Joel Goldstick wrote: [...] <<< Code NB Python v3.8 >>> def fp_range( start:float, stop:float, step:float=1.0 )->float: """Generate a range of floating-point numbers.""" if stop <= start: raise OverflowError( "RangeError: start must be less than stop" ) x = start while x < stop: yield x x += step This is almost the same solution as I came up with, but note that this is not quote the same as what Joel proposed. Repeated addition is not the same as multiplication with floating point numbers. To expand on this: it is because floating point numbers cannot represent all real numbers exactly. Repeated addition of a number represented inexactly will drift away from the mathematical value. Example: fp_range(0, 70, 0.07) as written above will yield 1001 numbers ending with 69.66 (i.e. approximately, but not quite, 70). Whereas Peter's version (below) will correctly yield 1000 numbers ending with 69.93. (Even then some numbers are not represented exactly, e.g. the last-but-one is 69.860001.) A generator based on Joel's idea would look like this: def fp_range(start, end, step): v = start n = int(math.ceil((end - start) / step)) for i in range(n): yield start + i * step -- https://mail.python.org/mailman/listinfo/python-list
Re: Error in lambda function..!
On 29/08/2020 08:58, Shivlal Sharma wrote:
from functools import*
nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
add = reduce(lambda a : a + 1, nums)
print(add)
error: -
TypeError Traceback (most recent call last)
in ()
1 from functools import*
2 nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]
> 3 add = reduce(lambda a : a + 1, nums)
4 print(add)
TypeError: () takes 1 positional argument but 2 were given
When you write
lambda a :
you are defining a function that takes **one** argument, viz. a.
It's the same as if you wrote:
def myfunc(a): return a+1
and then
add = reduce(myfunc, nums)
But reduce always calls your supplied function with **two** arguments.
Hence the error.
I don't know exactly what you are trying to do, but if you wrote
add = reduce(lambda a,b: a+1, nums)
or equivalently
def myfunc(a,b): return a+1
add = reduce(myfunc, nums)
reduce would call your lambda function succesively with arguments
(1,2) # which would return 1+1 i.e. 2
(2,3) # use previous result (2) and next number in the sequence
(3); returns 2+1 i.e. 3
(3,4) # use previous result (3) and next number in the sequence (4)
.
(8,9) # returns 8+! i.e. 9
and finally 9 would be printed.
If you want to watch what is happening try
def myfunc(a,b):
print(f"Myfunc({a},{b})")
return a+1
add = reduce(myfunc, nums)
Try
from functools import *
help(reduce)
for an explanation of how reduce works. Note that the first sentence
starts "Apply a function of two arguments"
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list
