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 _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/