[Python-Dev] Re: Nested functions and class scope

Guido van Rossum guido@python.org
Tue, 14 Nov 2000 09:00:39 -0500


> Guido van Rossum wrote:
> 
> > Looks like you are missing the point.  Of course I want to keep the
> > class as a local scope *inside the class def*.  

John Max Skaller replied:

> 	Ah, ok: seem's I was missing your intent.
> 
> > But I need it out of the way when executing methods.  
> 
> 	I don't think this is the right idea. Consider:
> 
> 	class X:
> 		x = 'x'
> 		def f(self): print x
> 	a = X()
> 	a.f() # should print 'x'

We understand each other perfectly.  I just disagree with you on what
this example should do!  I believe that in the sake of backwards
compatibility *as well as* not "giving in" to Java and C++ conventions
that I believe are a bad idea for Python, the reference to 'x' from
inside 'f' should not find the class variable 'x' but only a
module-global 'x'.

Backwards compatibility: currently this program prints '1':

    x = 1
    class C:
	x = 2
	def f(self): print x
    C().f()

I believe it should not be broken; I expect that this is, for better
or for worse, a popular idiom, especially when you look at methods
rather than varibles.  I believe it might be common to find this:

    from lower_level_module import some_call
    class C:
	def some_call(self):
	    "OO wrapper around some_call()"
	    return some_call(self.data)

Your rule would break this, because the reference to some_call()
inside C.some_call() would be changed to make a (senseless, recursive)
reference to itself instead of to the globally imported some_call()
function.

Java/C++ expectations: in Java/C++, the rules are different.  Inside a
method, not just names defined in the current class are in scope, but
also all names defined in its base classes.  Look at this:

    # Module a.py
    class B:
        def f(self): pass

    # Module b.py
    import a
    class C(a.B):
	def g(self): pass
        def h(self):
	    g(self)
            f(self)

Under your rules, the reference to g() in h() would work, but the
reference to f() would fail.  In similarly structured Java or C++
code, h() could reference both g() and f() without a naming
disambiguation.

Python requires that you specify the class when you reference a class
attribute (method or class variable).  I don't want to lose this rule,
it is important (to me) for readability.

[...more examples elided...]
> The point is that I think there is not really much choice
> in the matter: when you lookup an unqualified name in a function,
> you must look in the function locals, then in the scope of the
> object in which the function is defined, and then in the scope
> in which that is defined .. and so ontil you reach the bottom
> of the stack (which presumably is 'builtins').

I claim different, and above I have explained why I think this is
necessary.  I'd agree if you said "the scope of the *function* in
which it is defined."

> 	There's no real issue of keeping the class of the object
> of a bound method 'out of the way' when executing the function
> of the bound method. The scope of that class should be searched
> if, and only if, the function happens to have been defined
> inside that class. Note there is no such thing as a method
> in python: executing a bound (or unbound) method results
> in executing a plain old function.

If the code generator keeps track of the distinction between class and
function scopes, my rules can be implemented easily without
introducing a distinction between functions and methods.

> 	You _could_ exclude class scopes from the search stack
> associated with a function, and just search locals, then
> enclosing functions, then the enclosing module and finally
> builtins. But it would be highly inconsistent, and not what
> people used to lexical scoping would expect.


Yet, this is what I want.  It's impossible to design things so that
people coming from very different backgrounds will never encounter a
surprise.  Since Python is primarily an OO language, and there's ten
years of established Python rules, I prefer to give the OO people and
existing Python users an easier time (note that the OO people at least
can count on *consistently* not seeing the class scope in the
"methods") and tell the lexical-scoping people about the exceptions.

> It will be hard
> enough to explain the _consistent_ rule that only local
> bindings can be changed by assignments -- unless a global
> directive changes that. But that rule IS consistent (and the
> global directive much less so!)

> 	I think there is general agreement that lexically
> scoping functions inside functions will not break much code.
> Do you have an example of real application code where searching a class
> in which a function is defined will break code?

I haven't found one yet, but I've seen plenty of examples where a
class defines a method with the same name as a global function
(including lots in the standard library -- it really is a common
idiom).  I think this comes too close to risk breakage.

But even if we find that this doesn't break anything, I still think
it's unwise to allow unqualified access to class attributes from
within methods.  People would discover that in this example:

    class C:
	def m1(self):
	    pass
	def m2a(self):
	    m1(self)
	def m2b(self):
	    self.m1()

m2a() and m2b() would be equivalent, i.e. m1(self) and self.m1() are
equivalent.  That's evil, because it opens up a meaningless choice
between alternative (and the m1(self) notation is inferior, because it
doesn't look in base classes).

But I'm beginning to repeat myself, so I think I've said all that can
be said about this.  Sorry the scope rules in Vyper are wrong.

--Guido van Rossum (home page: http://www.python.org/~guido/)