On Fri, May 29, 2020 at 06:03:30PM +0200, Alex Hall wrote: > > I never said that Python's scoping rules were implementation details. > > > > I said that the storage mechanism of *how* local variables are stored, > > and hence whether or not writes to `locals()` are reflected in the > > local variables, is an implementation detail. > > I didn't know what it was you were saying back then, and I'm trying, but I > still haven't figured it out. I understand bits of it, but I don't know > what larger point you're trying to make. I think there are some things you > need to try harder to communicate clearly.
Okay, I will try to explain in more detail. I don't know if it will help or just make things more confusing, but I also replied to a post by Dominik covering this. Simplifying somewhat, we can say that under Python's execution rules, every undotted name must belong to at most one scope: - global and/or builtin; - an enclosing function; - the local function. So the compiler has to look each name up in only one of those places, counting globals and builtins as a single scope for this purpose. That is, as I understand it, a strict language guarantee and part of the execution model for Python 3. (Python 2 may have been a bit less strict.) How the compiler does the lookup, and whether the variables are stored as key:values in a mapping, or in a static array, or in a database, or written down as pencil marks on a Turing machine paper tape, is an implementation detail. But the scopes themselves are not. If the variables are implemented as key:values in a mapping, then an unbound variable is one where the key doesn't exist in the mapping. If they are implemented in a static array, then an unbound variable is one where that array cell happens to contain a special sentinel value (possibly a null pointer) to flag it as "unbound". (That's because a memory location cannot contain nothing, it must has some value.) But that distinction between "unbound and doesn't exist at all" versus "unbound but the cell exists" is just an implementation detail. Specifically: nothing in the language specification states that local variables MUST be cells of a static array. [...] > My point with that code snippet is that Python (not just some > implementations) can distinguish between a bound local, an unbound local, a > bound variable of some other type, and a name that isn't defined at all. Some pertinent observations: 1. Python cannot distinguish between bound and unbound until runtime. 2. The mechanism of how it distinuishes between bound and unbound is not part of the language, but is an implementation detail. E.g. is it a missing key or a special sentinel value like a null pointer, or something else? The language doesn't care. 3. As part of the execution model, Python enforces a rule that each undotted name belongs to a single scope. This rule uses lexical scoping, which means it can be perfectly and unambiguously determined at compile time. (For simplicity, I'm counting builtins and globals together for this purpose, and ignoring class scopes and comprehensions.) 4. You refer to "variable of some other type" -- in dynamic languages like Python, "type" normally refers to the value bound to the name, not the name itself. So I think it is better to use a different word to distiguish *kinds* of variables. I hope that you agree with those four points. You want to distinguish between "kinds of variables" like global, nonlocal, local: > I think labeling those as 'states' is pretty reasonable. There are two major reasons why labelling the scope of a variable "state" is not reasonable. Firstly, if it is a state of a variable, then in principle we ought to be able to mutate the state at runtime by some operation like "setting a flag" on the variable: declare x set state of x to global set state of x to local where at each stage there is exactly one variable x that is literally being moved from one scope to another scope (which may or may not require it to be moved in memory). I won't quite say that this is absurd, but it's hard to think of any reason why we would want this. And I certainly cannot think of any language which offers it as a language feature. Even if some language does support it, Python doesn't. Even in Lua, where a name can sometimes refer to a local variable and sometimes to a global within the same block of code, we're not changing the state of a single x, we're referring to two different variables with the same name but in different scopes. We can move variables from one scope to another but only by rewriting the code. That's not a runtime operation :-) The second reason why it is not helpful or meaningful to consider "local, nonlocal, global" to be distinct *kinds* (you said "types" but that is subject to confusion with typed variables in statically typed languages) of variables is that if they were different kinds of thing, they should have functional differences, but they don't. A constant and a variable are different kinds of things. Constants only support two operations: - set an initial value; - get that value; but all variables, regardless of scope, share a maximum of three fundamental operations: - set a value; - get the current value; - delete the variable. There are implementation differences that have observable effects on performance (local lookups are faster than global lookups); there are also practical differences as far as usability and usefulness (e.g. isolation, Globals Considered Harmful, thread safety etc). But fundamentally anything we can do with a local, we can do with a global, etc, all variables are the same kind of thing: def spam(arg): x = arg + 1 return x # Becomes: def spam(): global arg, x x = arg + 1 try: return x finally: del x (The calling conventions would be slightly different, but otherwise the functionality is the same.) Similarly we can replace globals with nonlocals; and if Python had persistent static local variables, we could replace nonlocals and globals with locals too. So globals, nonlocals and locals are pretty much functionally interchangable, except that locals are automatically deleted when the function returns, rather than being manually deleted. The difference is not in *what* they are, but *which scope* they belong to. Which is metadata about the variable, not part of the variable's state. -- Steven _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/FL25KUQVKABSHDZMOGUC5DBOZKLDB5Z7/ Code of Conduct: http://python.org/psf/codeofconduct/