Skip to content
Advertisement

Confusion about scoping in python classes

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?

Advertisement

Answer

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.

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement