On 27 March 2018 at 15:32, Joao S. O. Bueno <jsbu...@python.org.br> wrote: > Yes - but that would be the intention of the code beign written as in > your example - > > class C: > x = 1 > f = staticmethod(lambda: print(x)) > > While, the classic behavior can be attained by doing: > > class C: > x = 1 > f = classmethod(lambda cls: print(cls.x)) > > And the behavior in both cases if one of no-surprises for me. For > coders who don't have the mechanism of class creation in their > mind, that could come as a surprise, but it is better than being > inconsistent.
I wouldn't describe myself as "having the mechanism of class creation in my mind", but I'm not 100% sure that's a necessary criterion here. In an ideal world, Python's semantics is supposed to be intuitive, which means that understanding subtle details shouldn't be necessary to anticipate the behaviour of certain constructs. Looking at the following definitions: class C: x = 1 y1 = x y2 = (lambda: x)() def meth1(self): return self.x meth2 = lambda self: self.x @staticmethod def sm1(): return x sm2 = staticmethod(lambda: x) @classmethod def cm1(cls): return cls.x cm2 = classmethod(lambda cls: cls.x) I would expect meth1 and meth2 to be equivalent. I'd expect cm1 and cm2 to be equivalent. I would *not* expect sm1 to work, and I'd expect sm2 to fail for the same reason - namely that sm1 has no access to the class or an instance of it, so it should not be able to reference x. And so we should get NameError. These to me don't imply any sort of "understanding of how classes are created" - they simply need an understanding of how methods (normal, static and class) get access to the class/instance. They also require that you *don't* expect a class statement to create a scope that can be closed over. I didn't really think about that before I started analysing this code, but once I did, I realised that I've never expected that. So my reaction to a bare nonlocal variable reference in a method (whether defined in a def statement or as a lambda) would be "wait, what would that mean? I guess it's the global". I wouldn't even be looking at the x defined in the class at that point. The definition of y2 follows that rule as well - even though the fact that it's not defining a method, but it's "just" a bare lambda, slightly muddies the water. The odd one out is y1. I actually can't quite explain the logic that allows y1 to refer to the value of x, even though it's the most "natural" case. As I said, I don't think of a class as defining a new scope, rather I think of it as bundling together a set of statements that will be run to populate the class (maybe that's "having the mechanism of class creation in my mind"?). So I guess y1 = x is just normal statement sequencing. So I guess I'm saying that I don't really see a problem with the current behaviour here. Classes *don't* create a scope. So you don't close over class variables. For further emphasis of this point of view: @staticmethod def sm1(): nonlocal x return x gives nonlocal x ^ SyntaxError: no binding for nonlocal 'x' found which again I can understand as "there's no outer scope containing x". Having said all this, I guess you could say that I'm too close to the current behaviour to see its deficiencies. Maybe that's true. But I don't actually *use* any of this on a regular basis. I worked out all of the above based on intuition from how I understood Python's class model, plus a few small experiments that mostly just confirmed what I expected. If adding the ability to refer to a bare x in sm1/sm2 or y2 [1] means complicating the behaviour to the point where my mental model needs to involve injecting arguments into nested scopes, I'm a strong -1.If there's a need to "fix" comprehensions, then I'd much rather that we do so in a way that doesn't change the current behaviour or execution model. Specifically, I'd actually much *rather* that these 3 cases continue giving NameError. The comprehension cases: y3 = [x+i for i in (0,1,2)] #1 y4 = [1+i for i in (x, x+1)] #2 (#1 fails with NameError, and #2 works) strike me as odd corner cases that are a bit odd, but are not worth disrupting the normal cases for. And they should be viewed as oddities in how comprehensions look names up, and not as peculiarities of class scope. Sorry - I intended that to be an "it's all pretty simple and obvious" comment, but it turned into a rather long post. But I do think the basic idea remains simple. Paul [1] The other cases don't need to refer to a bare x, they already have perfectly good ways of accessing the class variable x. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/