Re: Best use of "open" context manager

2024-07-08 Thread Rob Cliffe via Python-list



On 06/07/2024 12:57, Oscar Benjamin via Python-list wrote:

On Sat, 6 Jul 2024 at 11:55, Rob Cliffe via Python-list
 wrote:

Consider this scenario (which I ran into in real life):
  I want to open a text file and do a lot of processing on the lines
of that file.
  If the file does not exist I want to take appropriate action, e.g.
print an error message and abort the program.
I might write it like this:

try:
  with open(FileName) as f:
  for ln in f:
  print("I do a lot of processing here")
  # Many lines of code here .
except FileNotFoundError:
  print(f"File {FileName} not found")
  sys.exit()

but this violates the principle that a "try" suite should be kept small,
so that only targeted exceptions are trapped,
not to mention that having "try" and "except" far apart decreases
readability.

This is catching a targeted exception (FileNotFoundError) so I think
it is fine. If the intention is just to call sys.exit() on error then
I wouldn't worry too much about having too much code in the try. Just
make sure that you do this in any other place where you open a file as
well.

One possible improvement is that you could catch the exception and use
its filename attribute:

except FileNotFoundError as e
  print(f"File {e.filename} not found")

That way if you did catch the wrong FileNotFoundError then at least
you print the correct filename.
Good point, Oscar - thank you.  (Even if you did omit the colon on the 
"except" line.  I've often thought we should have "Python without 
colons" as this is a mistake I frequently make.)


Alternatively:

except FileNotFoundError as e
  if e.filename != FileName:
   raise  # re-raise if not the intended exception
  print(f"File {e.filename} not found")

Indeed, that covers all basis.

For readability I would just move the many lines of code into a
separate function.
That may not always be convenient (e.g. if the many-lines-of-code needs 
to access a lot of local variables) but fair enough.

Thanks for your answer.
Rob Cliffe


The reason to avoid having too much code in the try mainly applies to
situations where you are going to do something other than call
sys.exit() and the exception is overly generic like ValueError or
TypeError. If the exception can easily be raised by a bug or something
other than the intended cause then it is bad to catch exceptions
around a larger block of code.

If it is expected that the caller of a function might have good reason
to catch the exception and handle it somehow then it is better to make
a dedicated exception class and raise that instead. When there is only
one place in the code that raises a particular exception type and only
one place that catches it then it is usually going to be clear that
you are catching the expected exception.

--
Oscar


--
https://mail.python.org/mailman/listinfo/python-list


Re: Best use of "open" context manager

2024-07-07 Thread Rob Cliffe via Python-list



On 07/07/2024 02:08, Cameron Simpson wrote:

On 06Jul2024 11:49, Rob Cliffe  wrote:

try:
    f = open(FileName) as f:
    FileLines = f.readlines()
except FileNotFoundError:
    print(f"File {FileName} not found")
    sys.exit()
# I forgot to put "f.close()" here -:)
for ln in File Lines:
        print("I do a lot of processing here")
        # Many lines of code here .


What about this:

    try:
        f = open(FileName) as f:
    except FileNotFoundError:
        print(f"File {FileName} not found")
        sys.exit()
    with f:
    ... process the lines here ...

Remember, the `open()` call returns a file object _which can be used 
as a context manager_. It is separate from the `with` itself.

Did you test this?
    f = open(FileName) as f:
is not legal syntax.
If you omit the "as f:"
it's legal, but doesn't work (trying to access the file after "with f" 
raises the same

    ValueError: I/O operation on closed file.
I'm using Python 3.11.5.

Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list


Best use of "open" context manager

2024-07-06 Thread Rob Cliffe via Python-list

Consider this scenario (which I ran into in real life):
    I want to open a text file and do a lot of processing on the lines 
of that file.
    If the file does not exist I want to take appropriate action, e.g. 
print an error message and abort the program.

I might write it like this:

try:
    with open(FileName) as f:
        for ln in f:
            print("I do a lot of processing here")
            # Many lines of code here .
except FileNotFoundError:
    print(f"File {FileName} not found")
    sys.exit()

but this violates the principle that a "try" suite should be kept small, 
so that only targeted exceptions are trapped,
not to mention that having "try" and "except" far apart decreases 
readability.


Or I might write it like this:

try:
    f = open(FileName) as f:
    FileLines = f.readlines()
except FileNotFoundError:
    print(f"File {FileName} not found")
    sys.exit()
# I forgot to put "f.close()" here -:)
for ln in File Lines:
        print("I do a lot of processing here")
        # Many lines of code here .

but this loses the benefits of using "open" as a context manager,
and would also be unacceptable if the file was too large to read into 
memory.


Really I would like to write something like

try:
    with open(FileName) as f:
except FileNotFoundError:
    print(f"File {FileName} not found")
    sys.exit()
else: # or "finally:"
        for ln in f:
            print("I do a lot of processing here")
            # Many lines of code here .

but this of course does not work because by the time we get to "for ln 
in f:" the file has been closed so we get

ValueError: I/O operation on closed file

I could modify the last attempt to open the file twice, which would 
work, but seems like a kludge (subject to race condition, inefficient).


Is there a better / more Pythonic solution?

Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list


win32clipboard writing to clipboard on Windows 11

2024-06-17 Thread Rob Cliffe via Python-list
Recently I acquired a new laptop running WIndows 11; my previous one 
uses WIndows 10.  I encountered a strange problem:
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.  The problem does not occur on my old 
machine (where I used the feature extensively).


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 "C:\TEST*.PY", line 8, in 
    SetClipboardData(CF_UNICODETEXT, "0")
pywintypes.error: (0, 'SetClipboardData', 'No error message is available')

Can anyone shed light on this?
Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list


Re: in Python? -- Chunk -- (ChunkC '(a a b b b)), ==> ((a 2) (b 3))

2024-06-11 Thread Rob Cliffe via Python-list

import itertools

def chunk1(seq):
    return [ ch * len(list(grp)) for (ch, grp) in itertools.groupby(s) ]

def chunk2(seq):
    return [ (ch, len(list(grp))) for (ch, grp) in itertools.groupby(s) ]

s='aaabbaa'
print(chunk1(s))
print(chunk2(s))
###
Program output:
['aaa', 'bb', '', 'aa']
[('a', 3), ('b', 2), ('c', 4), ('a', 2)]

Rob Cliffe

On 09/06/2024 22:20, HenHanna via Python-list wrote:


Chunk, ChunkC -- nice simple way(s) to write these in Python?


(Chunk  '(a a   b    a a a   b b))
    ==> ((a a) (b)  (a a a) (b b))


(Chunk  '(a a a a   b   c c   a a   d   e e e e))
    ==> ((a a a a) (b) (c c) (a a) (d) (e e e e))


(Chunk  '(2 2   foo   bar bar   j j j   k   baz baz))
    ==> ((2 2) (foo) (bar bar) (j j j) (k) (baz baz))

_

(ChunkC  '(a a   b b b))
 ==> ((a 2)  (b 3))

(ChunkC  '(a a   b  a a a   b b))
 ==> ((a 2)  (b 1)  (a 3)   (b 2))


--
https://mail.python.org/mailman/listinfo/python-list


Re: IDLE: clearing the screen

2024-06-08 Thread Rob Cliffe via Python-list

OK, here is the advanced version:
import os
class _cls(object):
    def __repr__(self):
        os.system('cls')
        return ''
cls = _cls()

Now when you type
cls
it clears the screen.  The only flaw is that there is a blank line at 
the very top of the screen, and the ">>>" prompt appears on the SECOND 
line.
(This blank line is because the IDLE prints the blank value returned by 
"return ''" and adds a newline to it, as it does when printing the value 
of any expression.)


Best wishes
Rob Cliffe
--
https://mail.python.org/mailman/listinfo/python-list


Re: Fwd: IDLE: clearing the screen

2024-06-05 Thread Rob Cliffe via Python-list



On 05/06/2024 04:09, Cameron Simpson wrote:

On 04Jun2024 22:43, Rob Cliffe  wrote:

import os
def cls(): x=os.system("cls")

Now whenever you type
cls()
it will clear the screen and show the prompt at the top of the screen.

(The reason for the "x=" is: os.system returns a result, in this case 
0.  When you evaluate an expression in the IDE, the IDE prints the 
result.  So without the "x=" you get an extra line at the top of the 
screen containing "0".)


Not if it's in a function, because the IDLE prints the result if it 
isn't None, and your function returns None. So:


    def cls():
    os.system("cls")

should be just fine.


Yes, you're right.
Rob Cliffe

--
https://mail.python.org/mailman/listinfo/python-list


Fwd: IDLE: clearing the screen

2024-06-04 Thread Rob Cliffe via Python-list

Welcome to Python!  A great language for program development.

Answers might be platform-dependent (are you using WIndows, Linux, etc.).
However, the following works for me on WIndows.  You can put it in the 
startup.py file so you don't have to type it every time you start up the 
IDLE.


import os
def cls(): x=os.system("cls")

Now whenever you type
cls()
it will clear the screen and show the prompt at the top of the screen.

(The reason for the "x=" is: os.system returns a result, in this case 
0.  When you evaluate an expression in the IDE, the IDE prints the 
result.  So without the "x=" you get an extra line at the top of the 
screen containing "0".)


I am sure that some jiggery-pokery could be used so you don't have to 
type the "()".  But that's more advanced ...


Best wishes
Rob Cliffe


On 04/06/2024 14:34, Cave Man via Python-list wrote:

Hello everyone,

I am  new to Python, and I have been using IDLE (v3.10.11) to run 
small Python code. However, I have seen that the output scrolls to the 
bottom in the output window.


Is there a way to clear the output window (something like cls in 
command prompt or clear in terminal), so that output stays at the top?



Thanks in anticipation!


--
https://mail.python.org/mailman/listinfo/python-list


Beep on WIndows 11

2023-11-11 Thread Rob Cliffe via Python-list

 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


Writing to clipboard in Python 3.11

2023-11-07 Thread Rob Cliffe via Python-list
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


Re: []=[]

2023-09-26 Thread Rob Cliffe via Python-list

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


Re: Tkinter ttk Treeview binding responds to past events!

2023-09-12 Thread Rob Cliffe via Python-list



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: Tkinter ttk Treeview binding responds to past events!

2023-09-11 Thread Rob Cliffe via Python-list



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


f-string error message

2023-08-30 Thread Rob Cliffe via Python-list

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


divmod with negative Decimal values

2023-08-22 Thread Rob Cliffe via Python-list

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


Re: Tkinter docs?

2023-05-30 Thread Rob Cliffe via Python-list
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


Tkinter docs?

2023-05-23 Thread Rob Cliffe via Python-list
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 

Re: Addition of a .= operator

2023-05-23 Thread Rob Cliffe via Python-list



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


Re: Addition of a .= operator

2023-05-23 Thread Rob Cliffe via Python-list



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

2023-05-23 Thread Rob Cliffe via Python-list



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


Learning tkinter

2023-05-18 Thread Rob Cliffe via Python-list

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: Question regarding unexpected behavior in using __enter__ method

2023-04-25 Thread Rob Cliffe via Python-list
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


Re: Packing Problem

2023-03-18 Thread Rob Cliffe via Python-list



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: python-list@python.org 
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: =- and -= snag

2023-03-14 Thread Rob Cliffe via Python-list



On 14/03/2023 21:28, avi.e.gr...@gmail.com 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: Cutting slices

2023-03-05 Thread Rob Cliffe via Python-list



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: Cryptic software announcements (was: ANN: DIPY 1.6.0)

2023-03-05 Thread Rob Cliffe via Python-list




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: Packing Problem

2023-03-02 Thread Rob Cliffe via Python-list
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


Packing Problem

2023-03-02 Thread Rob Cliffe via Python-list
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: How to fix this issue

2023-03-01 Thread Rob Cliffe via Python-list




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


Re: Python 3.10 Fizzbuzz

2023-02-27 Thread Rob Cliffe via Python-list



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: Line continuation and comments

2023-02-23 Thread Rob Cliffe via Python-list



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: semi colonic

2023-02-23 Thread Rob Cliffe via Python-list



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: semi colonic

2023-02-23 Thread Rob Cliffe via Python-list



On 23/02/2023 00:58, avi.e.gr...@gmail.com 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: Why doesn't Python (error msg) tell me WHAT the actual (arg) values are ?

2023-02-23 Thread Rob Cliffe via Python-list




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: LRU cache

2023-02-22 Thread Rob Cliffe via Python-list



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: semi colonic

2023-02-22 Thread Rob Cliffe via Python-list



On 23/02/2023 02:04, Thomas Passin wrote:

On 2/22/2023 7:58 PM, avi.e.gr...@gmail.com 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

2023-02-18 Thread Rob Cliffe via Python-list



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: Comparing caching strategies

2023-02-16 Thread Rob Cliffe via Python-list

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: evaluation question

2023-02-07 Thread Rob Cliffe via Python-list




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, mutt...@dastardlyhq.com 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 -, mutt...@dastardlyhq.com 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: Evaluation of variable as f-string

2023-02-07 Thread Rob Cliffe via Python-list
[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

2023-02-06 Thread Rob Cliffe via Python-list



On 02/02/2023 09:31, mutt...@dastardlyhq.com 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 -, mutt...@dastardlyhq.com 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

2023-01-31 Thread Rob Cliffe via Python-list

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

2023-01-30 Thread Rob Cliffe via Python-list



On 30/01/2023 09:41, mutt...@dastardlyhq.com wrote:

On Sun, 29 Jan 2023 23:57:51 -0500
Thomas Passin  wrote:

On 1/29/2023 4:15 PM, elvis-85...@notatla.org.uk wrote:

On 2023-01-28, Louis Krupp  wrote:

On 1/27/2023 9:37 AM, mutt...@dastardlyhq.com 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

2023-01-27 Thread Rob Cliffe via Python-list

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 of variable as f-string

2023-01-27 Thread Rob Cliffe via Python-list



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: bool and int

2023-01-27 Thread Rob Cliffe via Python-list



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

2023-01-27 Thread Rob Cliffe via Python-list



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: ok, I feel stupid, but there must be a better way than this! (finding name of unique key in dict)

2023-01-20 Thread Rob Cliffe via Python-list

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: Top level of a recursive function

2022-12-17 Thread Rob Cliffe via Python-list




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: Single line if statement with a continue

2022-12-17 Thread Rob Cliffe via Python-list



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: for -- else: what was the motivation?

2022-10-14 Thread Rob Cliffe via Python-list
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: python-list@python.org 
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: Byte arrays and DLLs

2022-07-03 Thread Rob Cliffe via Python-list

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'
 >>> ba
 bytearray(b'spam\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


Byte arrays and DLLs

2022-06-30 Thread Rob Cliffe via Python-list
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: REPL with multiple function definitions

2022-06-28 Thread Rob Cliffe via Python-list

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


REPL with multiple function definitions

2022-06-26 Thread Rob Cliffe via Python-list

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: Non-deterministic set ordering

2022-05-15 Thread Rob Cliffe via Python-list

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


Re: Non-deterministic set ordering

2022-05-15 Thread Rob Cliffe via Python-list



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


Non-deterministic set ordering

2022-05-15 Thread Rob Cliffe via Python-list
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: Printing Unicode strings in a list

2022-04-28 Thread Rob Cliffe via Python-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


Re: Style for docstring

2022-04-25 Thread Rob Cliffe via Python-list
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: Style for docstring

2022-04-22 Thread Rob Cliffe via Python-list
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: Behavior of the for-else construct

2022-03-05 Thread Rob Cliffe via Python-list




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


Re: Behavior of the for-else construct

2022-03-04 Thread Rob Cliffe via Python-list



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

2022-03-03 Thread Rob Cliffe via Python-list




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

2022-03-03 Thread Rob Cliffe via Python-list



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

2022-03-03 Thread Rob Cliffe via Python-list




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

2022-03-03 Thread Rob Cliffe via Python-list



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: python-list@python.org
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 
and read someth

Re: Behavior of the for-else construct

2022-03-03 Thread Rob Cliffe via Python-list



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, 

Re: Behavior of the for-else construct

2022-03-03 Thread Rob Cliffe via Python-list
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: python-list@python.org
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

2022-03-03 Thread Rob Cliffe via Python-list
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: All permutations from 2 lists

2022-03-03 Thread Rob Cliffe via Python-list




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: All permutations from 2 lists

2022-03-01 Thread Rob Cliffe via Python-list
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: Global VS Local Subroutines

2022-02-10 Thread Rob Cliffe via Python-list



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: Global VS Local Subroutines

2022-02-10 Thread Rob Cliffe via Python-list



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: A decade or so of Python programming, and I've never thought to "for-elif"

2021-12-01 Thread Rob Cliffe via Python-list
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: Negative subscripts

2021-11-26 Thread Rob Cliffe via Python-list

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: Unexpected behaviour of math.floor, round and int functions (rounding)

2021-11-20 Thread Rob Cliffe via Python-list




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: Unexpected behaviour of math.floor, round and int functions (rounding)

2021-11-20 Thread Rob Cliffe via Python-list



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: [tkinter][Windows]Unexpected state report for the tab key

2021-10-25 Thread Rob Cliffe via Python-list




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: importing a module from a file without the '.py' suffix

2021-10-22 Thread Rob Cliffe via Python-list

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: OT: AttributeError

2021-09-29 Thread Rob Cliffe via Python-list
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, 2qdxy4rzwzuui...@potatochowder.com wrote:

On 2021-09-29 at 09:21:34 +1000,
Chris Angelico  wrote:


On Wed, Sep 29, 2021 at 9:10 AM <2qdxy4rzwzuui...@potatochowder.com> 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: Problem with python

2021-09-04 Thread Rob Cliffe via Python-list

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: some problems for an introductory python test

2021-08-11 Thread Rob Cliffe via Python-list

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: Anaconda navigator not working

2021-06-20 Thread Rob Cliffe via Python-list
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: Strange disassembly

2021-06-19 Thread Rob Cliffe via Python-list




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: Strange disassembly

2021-06-19 Thread Rob Cliffe via Python-list



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: Why the list creates in two different ways? Does it cause by the mutability of its elements? Where the Python document explains it?

2021-06-14 Thread Rob Cliffe via Python-list

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: learning python ...

2021-05-24 Thread Rob Cliffe via Python-list
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: learning python ...

2021-05-24 Thread Rob Cliffe via Python-list



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: Style qeustion: Multiple return values

2021-04-12 Thread Rob Cliffe via Python-list

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: Friday Finking: initialising values and implied tuples

2021-04-05 Thread Rob Cliffe via Python-list



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: Friday Finking: initialising values and implied tuples

2021-04-05 Thread Rob Cliffe via Python-list



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

2021-04-05 Thread Rob Cliffe via Python-list



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

2021-04-05 Thread Rob Cliffe via Python-list



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, 2qdxy4rzwzuui...@potatochowder.com 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

2021-04-03 Thread Rob Cliffe via Python-list



On 03/04/2021 04:09, 2qdxy4rzwzuui...@potatochowder.com 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

2021-04-02 Thread Rob Cliffe via Python-list



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: memory consumption

2021-03-31 Thread Rob Cliffe via Python-list



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


REPL peculiarity

2021-03-11 Thread Rob Cliffe via Python-list

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


a + not b

2021-03-03 Thread Rob Cliffe via Python-list

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


Re: why sqrt is not a built-in function?

2021-01-15 Thread Rob Cliffe via Python-list



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


  1   2   >