[Python-ideas] Spelling of Assignment Expressions PEP 572 (was post #4)
Steven D'Aprano
steve at pearwood.info
Sun Apr 15 11:58:06 EDT 2018
On Sun, Apr 15, 2018 at 10:21:02PM +1000, Chris Angelico wrote:
> I don't think we're ever going to unify everyone on an arbitrary
> question of "expression first" or "name first". But to all the
> "expression first" people, a question: what if the target is not just
> a simple name?
>
> while (read_next_item() -> items[i + 1 -> i]) is not None:
> print("%d/%d..." % (i, len(items)), end="\r")
I don't see why it would make a difference. It doesn't to me.
> Does this make sense? With the target coming first, it perfectly
> parallels the existing form of assignment:
Yes, except this isn't ordinary assignment-as-a-statement.
I've been mulling over the question why I think the expression needs to
come first here, whereas I'm satisfied with the target coming first for
assignment statements, and I think I've finally got the words to explain
it. It is not just long familiarity with maths and languages that put
the variable first (although that's also part of it). It has to do with
what we're looking for when we read code, specifically what is the
primary piece of information we're initially looking for.
In assignment STATEMENTS the primary piece of information is the target.
Yes, of course the value assigned to the target is important, but often
we don't care what the value is, at least not at first. We're hunting
for a known target, and only when we find it do we care about the value
it gets.
A typical scenario: I'm reading a function, and I scan down the block
looking at the start of each line until I find the variable I want:
spam = don't care
eggs = don't care
self.method(don't care)
cheese = ... <<<==== HERE IT IS
so it actually helps to have the name up front. Copying standard maths
notation for assignment (variable first, value second) is a good thing
for statements.
With assignment-statements, if you're scanning the code for a variable
name, you're necessarily interested in the name and it will be helpful
to have it on the left.
But with assignment-expressions, there's an additional circumstance:
sometimes you don't care about the name, you only care what the value
is. (I expect this will be more common.) The name is just something
to skip over when you're scanning the code looking for the value.
# what did I pass as the fifth argument to the function?
result = some_func(don't care, spam := don't care, eggs := don't care,
self.method(don't care), cheese := HERE IT IS,
...)
Of course it's hard counting commas so it's probably better to add a bit
of structure to your function call:
result = some_func(don't care,
spam := don't care,
eggs := don't care,
self.method(don't care),
cheese := HERE IT IS,
...)
But this time we don't care about the name. Its the value we care about:
result = some_func(don't care,
don't care -> don't care
don't care -> don't care
don't care(don't care),
HERE IT IS .... ,
...)
The target is just one more thing you have to ignore, and it is helpful
to have expression first and the target second.
Some more examples:
# what am I adding to the total?
total += don't care := expression
# what key am I looking up?
print(mapping[don't care := key])
# how many items did I just skip?
self.skip(don't care := obj.start + extra)
versus
total += expression -> don't care
print(mapping[key -> don't care])
self.skip(obj.start + extra -> don't care)
It is appropriate for assignment statements and expressions to be
written differently because they are used differently.
[...]
> >>> items = [None] * 10
> >>> i = -1
> >>> items[i := i + 1] = input("> ")
> > asdf
> >>> items[i := i + 1] = input("> ")
> > qwer
> >>> items[i := i + 1] = input("> ")
> > zxcv
> >>>
> >>> items
> ['asdf', 'qwer', 'zxcv', None, None, None, None, None, None, None]
I don't know why you would write that instead of:
items = [None]*10
for i in range(3):
items[i] = input("> ")
or even for that matter:
items = [input("> ") for i in range(3)] + [None]*7
but whatever floats your boat. (Python isn't just not Java. It's also
not C *wink*)
> Are you as happy with that sort of complex
> expression coming after 'as' or '->'?
Sure. Ignoring the output of the calls to input():
items = [None] * 10
i = -1
items[i + 1 -> i] = input("> ")
items[i + 1 -> i] = input("> ")
items[i + 1 -> i] = input("> ")
which isn't really such a complex target. How about this instead?
obj = SimpleNamespace(spam=None, eggs=None,
aardvark={'key': [None, None, -1]}
)
items[obj.aardvark['key'][2] + 1 -> obj.aardvark['key'][2]] = input("> ")
versus:
items[obj.aardvark['key'][2] := obj.aardvark['key'][2] + 1] = input("> ")
Neither is exactly a shining exemplar of great code designed for
readability. But putting the target on the right doesn't make it worse.
--
Steve
More information about the Python-ideas
mailing list