Need help with Python scoping rules

Steven D'Aprano steve at
Wed Aug 26 17:32:38 CEST 2009

On Wed, 26 Aug 2009 10:57:32 +0000, kj wrote:

> In <7figv3F2m3p0dU1 at> "Diez B. Roggisch"
> <deets at> writes:
>>Classes are not scopes.
> This looks to me like a major wart, on two counts.
> First, one of the goals of OO is encapsulation, not only at the level of
> instances, but also at the level of classes.  Your comment suggests that
> Python does not fully support class-level encapsulation.

I don't even know what that is supposed to mean. If anything, your 
problem could be because Python has TOO MUCH "class-level encapsulation" 
compared to what you are expecting: functions inside a class don't see 
the class attributes you expect them too.

Actually, I think your problem is that you are interpreting what you're 
seeing in terms of C++ or Java, and assuming that whatever they do is the 
One True Definition of OO. That's hardly valid -- if any language 
deserves the label of the One True OO Design, it might be Smalltalk, on 
the basis that it was the first OO language. But even that is silly -- 
just because something was the first, that doesn't make it the best 
example of something.

> Second, my example shows that Python puts some peculiar restrictions on
> recursion.  

Incorrect. Recursive calls are just like any other function call in 
Python: when you call *any* function from the body of a function, it must 
be in scope at runtime.

def fact(n):
    print g()  # g must be in scope when fact is called
    if n < 2: return 1
    return n*fact(n-1)  # fact must be in scope when fact is called

Python doesn't treat recursive functions specially in any way. It is 
*classes*, not functions, that are special. This is explained in the docs:

It is also discussed in the PEP introducing nested scopes to Python:

It's even eluded to in the tutorial:

> Recursion!  One of the central concepts in the theory of
> functions!  This is shown most clearly by the following elaboration of
> my original example:
> class Demo(object):
>     def fact_rec(n):
>         if n < 2:
>             return 1
>         else:
>             return n * fact_rec(n - 1)

Why are you defining a method without a self parameter?

In any case, the problem has nothing to do with recursion:

>>> class Broken:
...     def f():
...             return g()
...     def g():
...             return "gee"
...     x = f()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in Broken
  File "<stdin>", line 3, in f
NameError: global name 'g' is not defined

>     def fact_iter(n):
>         ret = 1
>         for i in range(1, n + 1):
>             ret *= i
>         return ret
>     classvar1 = fact_iter(5)
>     classvar2 = fact_rec(5)
> This code won't compile as shown, 

That's nonsense. It compiles, and then it suffers a runtime error when 
you *execute* it.  (NameError occurs at runtime, not at compile time.) 
You can prove this for yourself by putting that above class definition 
inside a string, s:

>>> x = compile(s, '', 'exec')
>>> exec(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "", line 1, in <module>
  File "", line 15, in Demo
  File "", line 6, in fact_rec
NameError: global name 'fact_rec' is not defined

If you expect us to take your criticisms seriously, at least get your 
basic facts right.

> Is there any good reason (from the point of view of Python's overall
> design) for not fixing this?

It doesn't need to be "fixed" because it's not broken. The question is, 
should it be *changed* to match C++ programmers' expectations?


More information about the Python-list mailing list