Confusion about scoping in python classes - python

I am currently reviewing the python tutorials of python.org. I come from C++ and in the Classes tutorial (https://docs.python.org/3/tutorial/classes.html) I see that the scoping is similar to that in C++. It says the following about scoping and nesting:
"At any time during execution, there are at least three nested scopes whose namespaces are directly accessible:
- the innermost scope, which is searched first, contains the local names
- the scopes of any enclosing functions, which are searched starting with the nearest enclosing scope, contains non-local, but also non-global names
- the next-to-last scope contains the current module’s global names
- the outermost scope (searched last) is the namespace containing built-in names "
However I tried with following code from the same page:
class Dog:
tricks = []
def __init__(self, name):
self.name = name
def add_trick(self, trick):
self.tricks.append(trick) #this is the troublesome self
>>> d = Dog('Fido')
>>> e = Dog('Buddy')
>>> d.add_trick('roll over') #without the self this complains
>>> e.add_trick('play dead')
>>> d.tricks
['roll over', 'play dead']
If I remove the self in self.tricks.append(trick) the code will not compile and throw an NameError: name 'tricks' is not defined when calling the function d.add_trick('roll over').
Why does it happen? As I understand from the paragraph above, the function add_trick should look for a variable called tricks first within its own local scope, then if doesn't find any, in the nearest enclosing scope, which is the scope of the Class Dog, and there it should find it, without the need of using self. What am I missing?

As the tutorial said, scopes are searched in the order local, nonlocal, global, builtin.
The nonlocal scope is for enclosing functions. A class declaration is not a function. Its namespace gets thrown away after it is used to create the class object's __dict__, so variables at the class level cannot produce nonlocals in enclosed functions. Think of class-level assignments and variable reads like implicit assignments to and reads from a hidden dict, rather than as function locals. (Metaclasses can even replace this hidden dict with some other mapping.)
But the class scope does add one nonlocal, __class__. This is rarely used directly, but it's important for the zero-argument form of super().
This is the class object itself, so it's uninitialized until the class declaration finishes executing. So __class__.tricks would work inside a method if it's called after the class body executes (the usual case), but not if it's called during the execution of the class body.
There are other scopes to be aware of in Python. Comprehensions create a local scope like functions do. (They're basically compiled like generator functions--the kind with yield inside.) Also, caught exceptions are automatically deleted at the end of their handling clause to prevent a reference cycle.
You can see locals namespace using the locals() builtin and globals using globals(). The builtin scope is just the builtins module. The nonlocals are tricky. They'll actually show up in locals() if the compiler sees them being used. Function objects keep a reference to the nonlocals they use in their __closure__ attribute, which is a tuple of cells.

Your mistake is in thinking that the class is a scope. It isn't. As the doc you quoted explains, scopes are functions or modules.

Does this example help:
In [27]: foobar = 'pre'
In [28]: class AClass():
...: print('class def:', foobar)
...: foobar = 'class'
...: def __init__(self):
...: print('init:', foobar)
...: print(self.foobar)
...: self.foobar='instance'
...: print(self.foobar)
...:
class def: pre
In [29]: AClass.foobar
Out[29]: 'class'
Class definition is like running a function. foobar is initially the global value, but then gets reassigned, and is now available as part of the class namespace.
In [30]: x = AClass()
init: pre
class
instance
In [31]: x.foobar
Out[31]: 'instance'
In [32]: x.__class__.foobar
Out[32]: 'class'
When we define an instance, foobar comes from the outer, global namespace. self.foobar initially accesses the class namespace, but then gets redefined to be an instance variable.
Changing the global foobar is reflected in the next instance creation:
In [33]: foobar = 'post'
In [34]: y = AClass()
init: post
class
instance
In [35]: y.__class__.foobar
Out[35]: 'class'
Your tutorial page, in section 9.3.1. Class Definition Syntax says that while in the class definition, there's a new local namespace. That's where foobar='class' is defined. But once out of the class definition, that namespace is renamed as AClass.
The instance creation runs outside of the class definition. So it 'sees' the global foobar, not the class local one. It has to specify the namespace.
The distinction between scope when a class (or function) is defined, and when it is 'run' can be confusing.

A class definition in particular creates a class scope, which is a special kind of scope. While the class scope is indeed enclosed by the global scope (class variables can access global variables), the class scopes DO NOT ENCLOSE the local scopes within it! So, the local method scopes within the class body cannot access the class scope during name lookup. (At least without qualifying it). In other words, a class's scope is only accessible from within the top level codebody of its own class definition. it is inaccessible from anywhere else, be it inside or outside, without using dot notation directly

Related

In Python, do assignment operators access class or instance variables when passed as a default value in a class method definition?

for Python 3.7
I have a class with a class attribute (class-wide variable):
class foo:
var="value goes here"
as well as an instance variable created in the class's init method:
def __init__(self, var=var):
self.var=var
Passing class variable to parameter
The class variable has the same name as init's parameter, and this doesn't confuse the interpreter, because the interpreter treats any field to the left of an "=" sign as a new variable within the scope of the method. It accomplishes this by populating a new namespace (a dictionary of variables) for the method's scope, implemented either as an array: e.g. parameters[1] = "var" or an associative array: parameters['var'] = pointer_to_value. Then the interpreter looks inside the method body and substitutes the generic reference for any references to "var" that occur on the right side of an "=" sign. Actually, this is a lie, but it's simpler to understand that than what it really does:
The interpreter identifies the matching regular expression .*= *var(,{0,1}| *) *(;{0,1}|\n*) and then passes the corresponding pointer_to_value to the program's call stack). Because of that, the interpreter doesn't care what the parameters are named and is oblivious to the ambiguity of var=var. The fact that the ambiguity can be resolved is a side-effect of the structure of the language, rather than an intentional design decision. After all, ask yourself, when defining a method, why would you access a variable from inside of the method that you are defining? And why would you ever call an assignment operation to a variable in the parent scope from within a method definition? These are both illogical actions, and their namespace possibilities are mutually exclusive, so the interpreter never needs to address the ambiguity.
Conversely, the interpreter treats the right side of the "=" sign as an existing value and searches the class's namespace for variable definitions.
Storing parameter in Instance Variable
Within the method, the instance variable also has the same name as the class variable and the parameter, and this works inside the init method, because the instance variable is accessed via the self reference, e.g.
self.varname = varname
The problem
Method Definition
I need to access the instance variable from within the method definition of another method and I want to use the same name for this function's parameter:
def lookup(self, var=var):
print(var)
Will the expression var = var get the class attribute or the instance attribute? What does methodname(self) do with self, exactly? Is self a reference to an actual object, or does it only change the behavior of the interpreter from from static method to instance method? Does the interpreter automatically contextualize the right hand side of the "=" sign as an instance attribute of whatever object is typed into methodname(object)?
Method Body
Within the method body, if I make an assignment to var...
def lookup(self, var=var):
var = var
Will it store it in the class variable, the instance variable, or a new variable with the same name?
Will it get the class variable, instance variable, or the method's variable?
How do I explicitly reference these variables?
I have read the documentation and several OOP tutorials, and a recent book, and this is still not clear to me.
The only way you can access an instance variable is as an attribute of self.
When you just refer to var, that's never an instance variable; it's always a local, enclosing, global, or builtin variable.
In your method definition:
def lookup(self, var=var):
print(var)
… you have a parameter named var. Parameters are local variables. The print(var) inside the body prints that local variable.
What about this?
def lookup(self, var=var):
var = var
Again, var is a local variable—a parameter. So, you're just assigning the current value of that local variable to the same variable. Which has no useful effect, but of course it's perfectly legal.
Where does the parameter's value come from? At function call time, if you pass an argument, that argument gets bound to the parameter; if you don't, it gets filled in with the default value.
OK, so where does the default value come from?
At function definition time (when the def statement is executed), var is looked up in the current scope—that is, the body of the class definition—and its value is stored as a default in the function object (it should be visible as foo.lookup.__defaults__[0]).
So, the default value is "value goes here".
Notice that it's not a closure capture or other reference to the class attribute. When the class statement is executed, it uses that same class body namespace to build the class's attributes, so you end up with foo.var as another name for the same value that's in foo.lookup.__defaults__[0]. But they're completely independent names for that value; you can reassign foo.var = 3, and the default value for lookup's parameter will still be "value goes here".
So, to answer your specific questions:
Will it store it in the class variable, the instance variable, or a new variable with the same name?
None of the above. It stores it in a local variable that already exists, because it's a parameter.
Will it get the class variable, instance variable, or the method's variable?
If by "the method's variable" you mean the parameter, it's the last one.
How do I explicitly reference these variables?
The same way you explicitly reference anything else:
var is a local-enclosing-global-or-builtin variable.
self.var is an instance attribute, or a class attribute if there is no instance attribute.
type(self).var is a class attribute, even if there is an instance attribute.
The implicit method argument self is the instance. Thus, while self.var is the instance variable, you need to prefix with the class object to access the class variable. Sometimes, you may not know the class object and then you can simply use the __class__ attribute, i.e. the class variable is self.__class__.var.
To answer your three questions
1. Your assignment in the lookup method will create a new variable with the same name var.
2. It will get the argument that you passed to the method (I guess that's what you mean by "method's variable"
3. The code below shows how you can explicitly access these variables (is illustrates it in the __init__, but it doesn't really matter which method you are in.
class A(object):
var = 'class_variable'
def __init__(self, var=var):
print('Argument var={}'.format(var))
var = var
self.var = var
print('Instance var={}'.format(self.var))
print('Class var={}'.format(self.__class__.var))
print('Alternative access to Class var={}'.format(A.var))
This gives
>>> a = A()
Argument var=class_variable
Instance var=class_variable
Class var=class_variable
Alternative access to Class var=class_variable
>>> b = A('v')
Argument var=v
Instance var=v
Class var=class_variable
Alternative access to Class var=class_variable
>>> c = A()
Argument var=class_variable
Instance var=class_variable
Class var=class_variable
Alternative access to Class var=class_variable
I'm going to do my best to answer your questions but if I may isolate your problem first:
I want to use the same name for this function's parameter
Don't do this. There's no logical reason to do it and if it's confusing you now imagine how you'll feel when you look at your code in a few hours/days/weeks/months/years. Imagine how someone else looking at your code will feel.
For everything I write, imagine we have an instance named bob
This will cause bob.var to be "value goes here" every time it is instantiated. Every new object will have var set to "value goes here"
class foo:
var="value goes here"
This will cause bob.var to be whatever value gets passed to foo during instantiation. If nothing is passed, it will default to "value goes here"
def __init__(self, var=var):
self.var=var
This will print whatever is passed OR "value goes here" if nothing is passed
def lookup(self, var=var):
print(var)
This does nothing as the var in question is locally scoped and only exists until the end of the method
def lookup(self, var=var):
var = var
I hope this helps and just to reiterate: Use different variable names for your different variables.

Inner class usage python. Global name is not defined

I have a question about inner classes usage in python. I know that this is a bad practice but anyway. I am wondering about scope where inner class is defined. I got an error 'global name 'ClassName' is not defined'. My code snippet looks like this:
I discovered that to avoid getting this error I can use:
ClassWithEnum.EnumClass
instead of:
EnumClass
inside of doSomethingWithEnum() function. So I am wondering if there is any other way to make EnumClass defined inside of doSomethingWithEnum() function? And why there is no such error when I declare EnumClass as a default parameter in doSomethingWithEnum() function?
class ClassWithEnum(object):
class EnumClass(object):
...
def doSomethingWithEnum(self, m = EnumClass....):
...
Python class construction executes as code. The def statement is really a line of code being executed that creates a function. The class keyword introduces a namespace. Putting these two mechanisms together, it means that class EnumClass really creates an object by that name in the current namespace, not much different from what foo = 'bar' does, so within the same namespace you can refer to it by that name, which is what happens in the def statement.
Also compare:
class Foo:
bar = 'baz'
print bar
baz = bar
Every line of code inside a class block is a regular executable line of code.
Once your class definition is done, you're out of the ClassWithEnum namespace and cannot access EnumClass anymore simply by that name; it's now only available as ClassWithEnum.EnumClass; whether from "outside" the class or from within a function (any function, including class methods).
To get access to the class without typing its name from within the method you could do:
type(self).EnumClass
Or simply self.EnumClass, since properties are looked up up the chain.
When you are inside doSomethingWithEnum function, you are in the different namespace. To access anything that is defined in your class, like EnumClass, you should call self.EnumClass. If it were a class method, it would be cls.EnumClass.

Conditional statements in a class, but outside of scope of the function

We know that with notation:
class Foo(object):
a = 1
def __init__(self):
self.b = 2
def c(self):
print('c')
we can create static variable Foo.a, 'normal' variable b, which will be available after creating and instance of Foo, and method c
Today I was really surprised, that I can use conditional statements in a class, but outside of scope of the function
class C():
if True:
a = 1
b = 2
Languages like C++/Java, taught me that legal notation is similar to:
class Name():
variable = <expression>
Could you describe other rules, which refer to this specific scope? How I should name this scope?
The class body is just Python code. It has specific scope rules, but anything goes otherwise. This means you can create functions conditionally:
class C:
if some_condition:
def optional_method(self):
pass
or pull methods from elsewhere:
import some_module
class D:
method_name = some_module.function_that_accepts_self
etc.
The Python documentation for class definitions states:
A class definition is an executable statement.
and
The class’s suite is then executed in a new execution frame (see section Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains only function definitions.) When the class’s suite finishes execution, its execution frame is discarded but its local namespace is saved. A class object is then created using the inheritance list for the base classes and the saved local namespace for the attribute dictionary.
Note the usually in that text. Essentially, the class body is executed as a function would, and anything you put in the body namespace becomes an attribute on the class.
The Naming and binding section then tells you:
The scope of names defined in a class block is limited to the class block; it does not extend to the code blocks of methods
so names you define in this block cannot be directly accessed in methods; you'd use class.name or self.name instead.
In java everything is classes and object, classes are container but in python everything is object.
Classes are also objects. like functions(also objects) so when you use conditional statement in function then python allows you to do same in Classes.
like:-
def A():
if condition:
do something
elif condition:
do somethig
else:
do something
is same
Class A()
if condition:
do something
elif condition:
do somethig
else:
do something
you can assign functions even to store in a variable like you do for classes
def A():
pass
a = A # is valid
while in java you can't define function outside the classes.

namespace for class inside of class in python

I am trying to understand the nesting of namespaces in python 2.7.
Consider the following not-working code:
class c(object):
def debug(self):
print globals(),locals()
print x,y
class a(object):
x=7
def __init__(self):
self.y=9
class b(object):
def debug(self):
print globals(),locals()
print x,y
class d(c):
pass
How do I access to the variables x and y, from the calls
a().b().debug()
a.b().debug()
a().d().debug()
a.d().debug()
None of them seems able to see either x nor y.
EDIT after discussion, it seems that what I want (avoid reference to globals) can be reached only by explicitly extending the information on the nested classes. For instance
class a(object):
#
#classmethod
def _initClass(cls):
for v in dir(cls):
if type(cls.__dict__[v])==type:
setattr(cls.__dict__[v],'vader',cls)
#
x=7
class b(object):
def debug(self):
print b.vader.x
class d(c):
pass
a._initClass()
And even this technique does not seem useful to access an instance variable, as intended in the a().d().debug sequence.
class objects do not create a scope; class bodies are executed just once and discarded, the local namespace producing the class attributes. Class bodies never participate in nested scopes; they are ignored entirely when resolving non-local names.
As such, your x = 7 is not a name that the nested b class (or its methods) could ever access. Instead, it is a class attribute on a. Accessing a.x will work. y is an instance attribute, and you'll have to have a reference to the instance for it to be accessible anywhere else.
Note that this applies to class b as well; it is a class attribute on a now, just like x is a class attribute.
There is nothing special about your class b other than how you reference the class object. Just because you created an instance of a does not mean that b has any privileged access to it; a().b() does not create a relationship between the instances. You could do:
b = a.b
b_instance = b()
and there would be no difference. You could even do:
a.b.a = a
a.b.a()
and the world will not implode! (fancy that).
Martijn already gave a good answer, but I'll take a stab at my own.
As Martijn said, class declarations are executed rather like one-off functions, with their own local scope, when their module is first imported. Whatever's left over in this local scope, after everything in the class declaration has been executed, is used as the class's dictionary. So x, __init__, b, and d all end up being attributes on the a class object. The same is true of your classes b and d; their bodies are executed when the module is imported and the local scopes left over are used as their dictionaries as well.
The reason your code isn't working is that, by the time the debug method executes, the only scopes it's aware of are the global scope and its own local scope. In python, methods do not implicitly include a class instance's dictionary as part of their scope as they do in languages like C++. The class instance is available explicitly as the self argument which appears in the argument list (hence, Python's zen "explicit is better than implicit" -- try import this in the python console).
Also, it doesn't matter whether or not you instantiate a before accessing and instantiating the other classes b and d on a since b and d are both class attributes on a. Therefore, python can successfully retrieve b and d from either an instance of a or a direct reference to the a class object.

python scope of objects after instantiation

I am trying to learn OO Python, and I have a doubt regarding the scope of object attributes once it has been instantiated. So, I decided to write a very simple program:
class Struct:
def __init__(self):
self.resadd=[]
self.ressub=[]
self.resmul=[]
class MathStruct:
def mathss(self,x,y):
add=x+y
sub=x-y
mul=x*y
resstruct.resadd.append(add)
resstruct.ressub.append(sub)
resstruct.resmul.append(mul)
if __name__=='__main__':
resstruct=Struct()
for i in range(10,20):
mathsstuff=MathStruct()
mathsstuff.mathss(i,5)
print len(resstruct.resadd),resstruct.resadd
print len(resstruct.ressub),resstruct.ressub
print len(resstruct.resmul),resstruct.resmul
As you can see, the program is quite straightforward - but, here is my question - I instantiate the "result" object using:
resstruct=Struct()
and then use the resstruct.resadd, resstruct.ressub and resstruct.resmul attributes within the "mathss" function. Is this legal(I get the right answer!)? Does this mean that the object attributes are available even within other functions?
Normally, I would return values from the function and then append it to the object attribute, but I was (pleasantly) surprised to see that I can directly modify the object attribute within another function.
..In a nutshell, my question is what is the scope of the object attributes once it is instantiated? Sorry if this is a 101 question.
The line restruct = Struct() occurs at the module top level, outside any function or class, so it creates an object in the module global namespace. This namespace is, as the term implies, global to the module, and can be accessed from all functions. If you had done restruct = Struct() inside a function, it wouldn't be global and would disappear when the function finished.
In other words, if you had created it in a function scope, the object's lifetime would be limited to that scope. But you created it in global scope, so it lives forever. (An object's attributes have no scope of their own as such; they are inside that object's own instance namespace and are accessible only via the object. You can access obj.attr whenever you can access obj.)
(Note, though, that assigning to global variables from inside a function requires a global statement. You are able to access restruct because you only read the value of the variable and mutate that object with methods. If you tried to assign to restruct within a function, you would be creating a new local variable shadowing the global one.)

Categories

Resources