[Python-ideas] Spelling of Assignment Expressions PEP 572 (was post #4)
Steven D'Aprano
steve at pearwood.info
Fri Apr 13 09:18:59 EDT 2018
On Fri, Apr 13, 2018 at 09:56:35PM +1000, Chris Angelico wrote:
> How many times have people asked for "with (expr as name):" to
> be supported, allowing the statement to spread over multiple lines?
> With this syntax, it would suddenly be permitted - with dangerously
> similar semantics.
I see your point, but why don't we support "with (expr as name):" to
allow multiple lines? No, don't answer that... its off-topic. Forget I
asked.
In any case, we already allow similar syntax with different meaning in
different places, for example, something that looks just like assignment
inside expressions:
functions = [len, ord, map, lambda x, y=1: x+y]
But its not really an assignment as such, its a parameter declaration.
If we agree that the benefit of putting the expression first is
sufficiently large, or that the general Pythonic look of "expr as name"
is sufficiently desirable (it just looks and reads nicely), then we can
afford certain compromises. Namely, we can rule that:
except expr as name:
with expr as name:
continue to have the same meaning that they have now and never mean
assignment expressions. Adding parens should not change that.
If you try to write something like:
except (spam or eggs as cheese) or function(cheese) as name:
with (spam or eggs as cheese) or function(cheese) as name:
etc (or any other assignment expression, effectively anything which
isn't currently allowed) then you get a syntax error.
So this:
with expr as name:
process(name)
will only work if expr returns an object with a context manager. But
that's they way it works now, so nothing really changes.
In other words, the rule is that "expr as name" keeps its current, older
semantics in with and except statements, and NEVER means the new, PEP
572 assignment expression.
Yes, that's a special case that breaks the rules, and I accept that it
is a point against "as". But the Zen is a guideline, not a law of
physics, and I think the benefits of "as" are sufficient that even
losing a point it still wins.
> For many MANY context managers, "with (expr as
> name):" would do the exact same thing as "with expr as name:". There
> is a general expectation that adding parentheses to an expression
> usually doesn't change the behaviour, and if it's legal, people will
> assume that the behaviour is the same. It isn't, and it's such a
> sneaky difference that I would call it a bug magnet.
Indeed. I wouldn't allow such a subtle difference in behaviour due to
parens. That reminds me of the Python 1 and early 2.x except clauses,
where
except ValueError, TypeError:
except (ValueError, TypeError):
meant different things. I still shudder at that one.
> So if it's a bug magnet, what do we do?
>
> 1) Permit the subtly different semantics, and tell people to be careful
No.
> 2) Forbid any use of "(expr as name)" in the header of a 'with' statement
You can't forbid it, because it is currently allowed syntax (albeit
currently without the parens). So the rule is, it is allowed, but it
means what it meant pre-PEP 572.
> 3) Forbid it at top level, but permit it deeper down
I don't know what that means. But whatever it means, probably no :-)
> 4) Something else??
Well, there's always the hypothetical -> arrow binding operator, or the
Pascal := assignment operator (the current preference).
I don't hate the := choice, I just think it is more Pascal-esque that
Pythonic :-)
> > I don't especially dislike := but I really think that putting the
> > expression first is a BIG win for readability. If that requires parens
> > to disambiguate it, so be it.
>
> There's a mild parallel between "(expr as name)" and other uses of
> 'as', which bind to that name. Every other use of 'as' is part of a
> special syntactic form ('import', 'with', and 'except'), but they do
> all bind to that name. (Point of interest: You can "with expr as
> x[0]:" but none of the other forms allow anything other than a simple
> name.)
I disagree: I think it is a strong parallel. They're both name bindings.
How much stronger do you want?
True, we don't currently allow such things as
import math as maths, mathematics, spam.modules[0]
but we could if we wanted to and there was a sensible use-case for it.
> There's a strong parallel between "target := value" and "target
> = value";
Sure. And for a statement, either form would be fine. I just think that
in an expression, it is important enough to bring the expression to the
front, even if it requires compromise elsewhere.
[...]
> I actually can't find anything about the -> operator, only the <- one.
> (Not that I looked very hard.) Is it a truly viable competitor, or
> just one that you'd like to see mentioned for completeness?
Yes, as I mentioned in another post, R allows both -> and <-, some
language called BETA uses ->, various calculator BASICs use -> (albeit
with a special single character, not a digraph) as does HP RPN.
Here's an example from R:
> c(1, 2, 3+4, 5) -> data
> data
[1] 1 2 7 5
But whether it is viable or not depends on *us*, not what other
languages do. No other language choose the syntax of ternary if
expression before Python used it. We aren't limited to only using syntax
some other language used first.
> > I think that there should be more attention paid to the idea of putting
> > the expression first, rather than the name.
>
> How many ways are there to bind a value to a name?
[...]
> I'm seeing consistency here in that *EVERY* name binding where the
> name is at the end uses "as target" as its syntax. Everything else
> starts with the target, then defines what's being assigned to it. So I
> don't see much value in a "->" operator, except for the mere fact that
> it's different (and thus won't conflict in except/with); and the bulk
> of name bindings in Python put the name first.
We shouldn't be choosing syntax because other syntax does the same. We
should pick the syntax which is most readable and avoids the most
problems. That's why Guido bucked the trends of half a century of
programming languages, dozens of modern languages, and everything else
in Python, to put the conditional in the middle of ternary if instead of
the beginning or end. (And he was right to do so -- I like Python's
ternary operator, even if other people think it is weird.)
If people agree with me that it is important to put the expression first
rather than the target name, then the fact that statements and for loops
put the name first shouldn't matter.
And if they don't, then I'm outvoted :-)
--
Steve
More information about the Python-ideas
mailing list