osdir.com


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Python-Dev] Examples for PEP 572


On 7/4/2018 9:35 AM, Steven D'Aprano wrote:
> On Wed, Jul 04, 2018 at 05:02:07PM +1000, Chris Angelico wrote:
>> On Wed, Jul 4, 2018 at 4:07 PM, Serhiy Storchaka <storchaka at gmail.com> wrote:
> 
>> "Assignment is a statement" -- that's exactly the point under discussion.

I believe that this is Chris quoting and commenting on Serhiy having 
said 'assigment is a statement'

> Not any more it isn't. We've now gone from discussion to bitter
> recriminations *wink*

I don't see any recrimination in what either said.

>> "del is a statement" -- yes, granted
>>
>> "function and class declarations are statements" -- class, yes, but
>> you have "def" and "lambda" as statement and expression equivalents.
> 
> Even class can be re-written as a call to type(), if you need to. It's
> probably not practical to do so in anything but the simplest cases, but
> it is there.

Serhiy's viewpoint is a legitimate one.  A major difference between def, 
class, and import statements and equivalent lambda, type, and __import__ 
expressions is that the latter do not name-bind the resulting object. 
This will continue to be true.

There is, however, precedent for subverting "assignment is a statement".

 >>> class C():
	pass

 >>> c = C()
 >>> c.a = 1
 >>> setattr(c, 'b', 2)
 >>> c.b
2

However, the target name, when given explicitly, is quoted.  This 
preserves the general rule that non-keyword identifiers in expressions 
are immediately evaluated.  The current exceptions are the 'and', 'or', 
and 'if-else' constructs that embed flow control in expressions.  But if 
a name in such expressions is not ignored, it is evaluated normally.

The purpose of setattr is to allow the target attribute name to be any 
variable or expression that evaluates to a string, so that one can do 
the following.

 >>> d = 'c'
 >>> setattr(c, d+'2', 3)
 >>> c.c2
3

or even

 >>> setattr(c, input('aname: '), 33)
aname: qr
 >>> c.qr
33

(An allowed side-effect is the possibility of adding attributes, such as 
'2e', that can only be recovered with getattr.)

The same comments apply to globals().update({'foo':42}), pointed out by 
David Merz in another post.  (This specific form does not work does not 
work within functions, as pointed out by Chris Angelico.  But others do.)


A major difference between a while loop statement and an equivalent tail 
recursion call (expression) is that while loops do explicit assignment 
in the current namespace while recursive calls do implicit assignment in 
a new (and unneeded) execution frame.  The targets are the unquoted 
parameter names in the function header.  I regard the the easy use of 
unevaluated unquoted names as a justification for having statements in 
addition to expressions.


An alternate assignment-within-expressions proposal would be to follow 
the setattr precedent.  Add a builtin function 'set' with paramenters 
'name' and 'value'.  It would have to be built-in because Python code 
cannot directly access function local namespaces.  The problem, aside 
from extra typing, is that efficient implementation of functions 
requires that all local names be known at compile time.  But a name 
added by "set(input('name: '), <expr>)" is impossible to know.

Making the assignment target be an unquoted unevaluated non-keyword name 
immediately followed by ':=' solves this.  But it either introduces a 
new and different type of exception to 'names in expressions are 
evaluated', or one must regard 'assignment expression' as a new 
statement-expression hybrid.  Advocates of assignment expressions should 
not really be surprised that this disquiets some people.

-- 
Terry Jan Reedy