1

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?

Leo
  • 53
  • 7

4 Answers4

3

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.

gilch
  • 10,813
  • 1
  • 23
  • 28
  • Thank you, very thorough answer!! It really answered my question and shed some light to this amazing language. – Leo Oct 06 '19 at 11:33
1

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.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • https://stackoverflow.com/questions/6167923/block-scope-in-python then why does this answer say classes create scope – astralwolf Sep 15 '21 at 15:09
0

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.

hpaulj
  • 221,503
  • 14
  • 230
  • 353
0

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

astralwolf
  • 315
  • 1
  • 3
  • 15