Well, there is an idiom to "keep everything as is", and work around the current limitations:
class A: def b(): x = 1 d = [i + x for i in range(2)] return locals() locals().update(b()) del b Maybe if we could find a syntactic sugar for this idiom (with an abuse of the `with` keyword, for example), people could be happy, with class bodies working by default as they are now, and enabling the reuse of defined members by using the special syntax - class A: with class: x = 1 d = [i + x for in range(2)] On 27 March 2018 at 12:27, Paul Moore <p.f.mo...@gmail.com> wrote: > 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/