第721页 | Learning Python | 阅读 ‧ 电子书库

同步阅读进度,多语言翻译,过滤屏幕蓝光,评论分享,更多完整功能,更好读书体验,试试 阅读 ‧ 电子书库

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.

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()

请支持我们,让我们可以支付服务器费用。
使用微信支付打赏


上一页 · 目录下一页


下载 · 书页 · 阅读 ‧ 电子书库