On Fri, Apr 28, 2017 at 03:30:29PM +1000, Chris Angelico wrote:
Obviously we can define syntax to do anything we like, but what is the logical connection between the syntax and the semantics? What part of "function parameter list" suggests "assign attributes to arbitrary objects"?
What part of a 'for' loop suggests that you can do this?
I'm not sure what "this" is supposed to do. You've written some obscure but perfectly legal Python code:
odds = *10; evens = *10 for idx, (odds if idx%2 else evens)[idx//2] in stuff: ...
My guess is that "this" refers to the side-effect that assigning to a list item updates the list item. Um, yeah, it does. What's your point? Of course it does. That's what its supposed to do.
Perhaps you think that there's something weird about using an arbitrary assignment target as the for-loop. I don't know why you think that's weird. Here's a simpler example:
for obj.attr in seq: ...
Yes, its a bit unusual to do that, but its not weird. The assignment target for a loop is just an ordinary assignment target, and the assignment occurs during the execution of code, just like any other assignment that occurs inside the body of the function.
What is weird is to have the function *declaration* have global side effects and perform assignments outside of the function. The parameter list is *not* a general assignment statement, it is a declaration of what local variables will be assigned to when you call the function.
In Python 3, function paramaters are plain (non-qualified) identifiers, not general assignment targets. Even in Python 2, the most that was supported were tuple-unpacking. Even that can be read as conceptually just a declaration:
def func(a, (x,y)):
declares that the first argument is a, and the second argument is a pair of values x and y. But this proposal has to be read as a declaration plus a separate assignment:
def func(a, spam.x):
declares that the second argument is called "x", (but not spam.x), and *in addition* there will be an assignment spam.x = x at some point, presumably before the function body gets entered.
Nothing whatsoever says that this is a good idea, but it's perfectly legal, because the for loop is defined in terms of assignment. If this were to be accepted (which, fwiw, I'm not actually advocating, but IF), it would also be defined in terms of assignment.
Why should it be defined in terms of general assignment? That's the point I'm making. While function sigs are a form of assignment, they're more of a declaration than an executable statement like the other binding statements. There's a superficial connection, but they really are quite different things.
(For example: if you allow spam.foo as a parameter, that can call arbitrary Python code in spam.__setattr__, which assigning to foo as a parameter will not do.)
You still shouldn't be assigning to arbitrary objects, especially not randomly rebinding module names, but it's easy to grok the assignment equivalence.
You need to speak to more beginners if you think the connection between spam.x and x is obvious:
def func(spam.x): print(x)
Where is x declared? It looks like there's a local spam.x which isn't used, and a global x that is. But that's completely wrong. Without the context of somebody telling you "this is syntax for magically assigning to self.attributes in the constructor", I believe this will be unfathomable to the average non-expert.
And yes, it WOULD reinstate the argument unpacking removed by PEP 3113. So for this to go forward, the objections in that PEP would have to be addressed.
Nicely remembered :-)