预计阅读本页时间:-
Methods, Classes, and Nested Scopes
This gotcha went away in Python 2.2 with the introduction of nested function scopes, but I’ve retained it here for historical perspective, for readers working with older Python releases, and because it demonstrates what happens to the new nested function scope rules when one layer of the nesting is a class.
Classes introduce local scopes, just as functions do, so the same sorts of scope behavior can happen in a class statement body. Moreover, methods are further nested functions, so the same issues apply. Confusion seems to be especially common when classes are nested.
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
In the following example (the file nester.py), the generate function returns an instance of the nested Spam class. Within its code, the class name Spam is assigned in the generate function’s local scope. However, in versions of Python prior to 2.2, within the class’s method function the class name Spam is not visible—method has access only to its own local scope, the module surrounding generate, and built-in names:
def generate(): # Fails prior to Python 2.2, works later
class Spam:
count = 1
def method(self): # Name Spam not visible:
print(Spam.count) # not local (def), global (module), built-in
return Spam()
generate().method()
C:\python\examples> python nester.py
...error text omitted...
Print(Spam.count) # Not local (def), global (module), built-in
NameError: Spam
This example works in Python 2.2 and later because the local scopes of all enclosing function defs are automatically visible to nested defs (including nested method defs, as in this example). However, it doesn’t work before 2.2 (we’ll look at some possible solutions momentarily).
Note that even in 2.2 and later, method defs cannot see the local scope of the enclosing class; they can only see the local scopes of enclosing defs. That’s why methods must go through the self instance or the class name to reference methods and other attributes defined in the enclosing class statement. For example, code in the method must use self.count or Spam.count, not just count.
If you’re using a release prior to 2.2, there are a variety of ways to get the preceding example to work. One of the simplest is to move the name Spam out to the enclosing module’s scope with a global declaration. Because method sees global names in the enclosing module, references to Spam will work:
def generate():
global Spam # Force Spam to module scope
class Spam:
count = 1
def method(self):
print(Spam.count) # Works: in global (enclosing module)
return Spam()
generate().method() # Prints 1
A better alternative would be to restructure the code such that the class Spam is defined at the top level of the module by virtue of its nesting level, rather than using global declarations. The nested method function and the top-level generate will then find Spam in their global scopes:
def generate():
return Spam()
class Spam: # Define at top level of module
count = 1
def method(self):
print(Spam.count) # Works: in global (enclosing module)
generate().method()
In fact, this approach is recommended for all Python releases—code tends to be simpler in general if you avoid nesting classes and functions.
If you want to get complicated and tricky, you can also get rid of the Spam reference in method altogether by using the special __class__ attribute, which returns an instance’s class object:
def generate():
class Spam:
count = 1
def method(self):
print(self.__class__.count) # Works: qualify to get class
return Spam()
generate().method()