I'm new to Python, targeting version 2.6 with forward compatibility to 3.x in mind. I am trying to wrap my head around what access to what variables are accessible from where. I already invested a lot of time in reading/understanding this topic, ultimately I would like to confirm the accuracy of my understanding of this topic.
Here's what I think I've got:
You can think of the "top level" of a Python script as being a "root object" that contains everything else in the program. It's like other instantiated objects, but has special features:
you can access its members (variables, functions, classes) from any function or class, without "self"
you can reassign its members from within any class or function by first using "global"
Any function/method, no matter how deeply nested, has access to all variables assigned anywhere above it. If the variable is a mutable object, its members can be changed, regardless of whether the variable is a member of the root object or more deeply nested.
If an existing variable name is used for assignment within a function, a new local variable with the same name, limited in scope to the function, is created. If there are multiple local variables with the same name in nested functions, the access is to the nearest enclosed local variable.
If the variable is a member of the root object (i.e. exists at the top level of the script) and "global" is used before the variable name is assigned, then no new local variable is created, so assignment applies to the member of the root object. In Python 3, "nonlocal" can be used similarly to have a variable assignment apply to the member of nearest enclosing function where the variable is assigned.
Because functions are created once upon execution, an object used as a default parameter assignment will persist across repeated calls to the function. If the object is mutable, changes to its members will also persist across calls to the function if the function doesn't reassign the variable name to another value.
The rules above apply inside a class, except that you need to refer to class members from within its methods via "self", and from outside the class via a reference to the class or an instantiation of the class.
You can execute statements in classes outside methods, like the "root object" does; however, they are executed immediately upon executing the class definition, not during instantiation. Class members can then be accessed from outside the class by referring to the class itself. It's as if when the class is defined, it is instantiated as as "class object" using its own name for the object, without calling the constructor. Further instantiations are shallow copies of that initial instantiation (the class object), in whatever state it exists, and execute the constructor if present. When referring to the class object rather than an instantiation, variables are accessible, but methods can't be called unless they are decorated with @classmethod (which allows access to class members) or @staticmethod (which doesn't).
Only references to the class members are copied during instantiation, so any assignments not within a method are "class variables" shared between all instantiations of the class. As with a function, assignments to the same variable name in an instantiated object will create a new variable limited in scope to that object, but changes to members of mutable objects will be seen across all instantiations of the class, including the class object, because the variables within those objects all refer to the same mutable object. To prevent variables from being shared across instantiations of the same class, they can be assigned within a method, using "self".
The class exists within whatever scope it is defined (e.g. it can be inside a function, and would be inaccessible outside of that unless the function returns the reference to the class, or assigns it to an outside variable it has access to, e.g. via global/nonlocal, or a class variable, or a member of a mutable object).
So, do I understand Python scoping yet? Thanks for any and all insight.