On 17 April 2018 at 17:46, Chris Angelico <ros...@gmail.com> wrote: > Syntax and semantics > ==================== > > In any context where arbitrary Python expressions can be used, a **named > expression** can appear. This is of the form ``target := expr`` where > ``expr`` is any valid Python expression, and ``target`` is any valid > assignment target.
The "assignment expressions should be restricted to names only" subthread from python-ideas finally crystallised for me (thanks in part to your own comment that 'With regular assignment (whether it's to a simple name or to a subscript/attribute), removing the "target :=" part will leave you with the same value - the value of "x := 1" is 1.'), and I now have a concrete argument for why I think we want to restrict the assignment targets to names only: all complex assignment targets create inherent ambiguity around the type of the expression result, and exactly which operations are performed as part of the assignment. Initially I thought the problem was specific to tuple unpacking syntax, but attempting to explain why subscript assignment and attribute assignments were OK made me realise that they're actually even worse off (since they can execute arbitrary code on both setting and retrieval, whereas tuple unpacking only iterates over iterables). Tackling those in order... Tuple unpacking: What's the result type for "a, b, c := range(3)"? Is it a range() object? Or is it a 3-tuple? If it's a 3-tuple, is that 3-tuple "(1, 2, 3)" or "(a, b, range(3))"? Once you have your answer, what about "a, b, c := iter(range(3))" or "a, b, *c := range(10)"? Whichever answers we chose would be surprising at least some of the time, so it seems simplest to disallow such ambiguous constructs, such that the only possible interpretation is as "(a, b, range(3))" Subscript assignment: What's the final value of "result" in "seq = list(); result = (seq[:] := range(3))"? Is it "range(3)"? Or is it "[1, 2, 3]"? As for tuple unpacking, does your preferred answer change for the case of "seq[:] := iter(range(3))"? More generally, if I write "container[k] := value", does only "type(container).__setitem__" get called, or does "type(container).__getitem__" get called as well? Again, this seems inherently ambiguous to me, and hence best avoided (at least for now), such that the result is always unambiguously "range(3)". Attribute assignment: If I write "obj.attr := value", does only "type(obj).__setattr__" get called, or does "type(obj).__getattribute__" get called as well? While I can't think of a simple obviously ambiguous example using builtins or the standard library, result ambiguity exists even for the attribute access case, since type or value coercion may occur either when setting the attribute, or when retrieving it, so it makes a difference as to whether a reference to the right hand side is passed through directly as the assignment expression result, or if the attribute is stored and then retrieved again. If all these constructs are prohibited, then a simple design principle serves to explain both their absence and the absence of the augmented assignment variants: "allowing the more complex forms of assignment as expressions makes the order of operations (as well as exactly which operations are executed) inherently ambiguous". That ambiguity generally doesn't exist with simple name bindings (I'm excluding execution namespaces with exotic binding behaviour from consideration here, as the consequences of trying to work with those are clearly on the folks defining and using them). > The value of such a named expression is the same as the incorporated > expression, with the additional side-effect that the target is assigned > that value:: > > # Handle a matched regex > if (match := pattern.search(data)) is not None: > ... > > # A more explicit alternative to the 2-arg form of iter() invocation > while (value := read_next_item()) is not None: > ... > > # Share a subexpression between a comprehension filter clause and its > output > filtered_data = [y for x in data if (y := f(x)) is not None] [snip] > Style guide recommendations > =========================== > > As this adds another way to spell some of the same effects as can already be > done, it is worth noting a few broad recommendations. These could be included > in PEP 8 and/or other style guides. > > 1. If either assignment statements or assignment expressions can be > used, prefer statements; they are a clear declaration of intent. > > 2. If using assignment expressions would lead to ambiguity about > execution order, restructure it to use statements instead. > > 3. Chaining multiple assignment expressions should generally be avoided. > More than one assignment per expression can detract from readability. Given the many different uses for ":" identified on python-ideas, I'm inclined to suggest making these proposed style guidelines more prescriptive (at least initially) by either: 1. Listing out specific approved unambiguous use cases (i.e. if statement conditions, while loop conditions, list comprehensions, generation expressions) 2. Making the 3rd admonition more general by advising against using ":" for more than one purpose in the same expression (i.e. don't combine assignment expressions with slicing syntax, lambda expressions, function headers, variable annotations, dict or set displays, dict or set comprehensions) Cheers, Nick. -- Nick Coghlan | ncogh...@gmail.com | Brisbane, Australia _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com