关于python类范围界定的困惑

时间:2019-10-05 15:50:11

标签: python scope self

我目前正在查看python.org的python教程。我来自C ++,在“类”教程(https://docs.python.org/3/tutorial/classes.html)中,我发现其作用域与C ++中的作用域相似。它说明了有关范围界定和嵌套的以下内容:

“在执行期间的任何时间,至少有三个嵌套作用域可以直接访问其名称空间:
-最先搜索的最内部作用域包含本地名称
-从最接近的封闭范围开始搜索的任何封闭函数的范围都包含非本地名称,但也包含非全局名称
-倒数第二个范围包含当前模块的全局名称
-最外面的作用域(最后搜索)是包含内置名称“

的名称空间

但是我尝试使用同一页面中的以下代码:

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']

如果我删除了self中的self.tricks.append(trick),则在调用函数NameError: name 'tricks' is not defined时,代码将不会编译并抛出d.add_trick('roll over')

为什么会发生?从上面的段落中可以了解到,函数add_trick首先应在其自身的本地范围内查找名为tricks的变量,然后在最接近的范围内查找变量self。范围狗,应该找到它,而无需使用trim。我想念什么?

3 个答案:

答案 0 :(得分:2)

您的错误在于认为班级是一个范围。不是。正如您引用的文档所解释的,作用域是函数或模块。

答案 1 :(得分:1)

如本教程所述,范围是按本地,非本地,全局,内置的顺序搜索的。

非本地作用域用于封闭函数。类声明不是函数。在用于创建类对象的[L]后,其名称空间将被丢弃,因此类级别的变量无法在封闭函数中产生非局部变量。将类级别的赋值和变量读取想像成对隐藏dict的隐式赋值和从中读取,而不是作为函数局部变量。 (元类甚至可以用其他映射替换此隐藏的字典。)

但是类范围确实添加了一个非本地__dict__。很少直接使用它,但对于__class__的零参数形式来说很重要。

这是类对象本身,因此它尚未初始化,直到类声明完成执行为止。因此,如果super()在类主体执行之后被称为之后(通常情况下),则该方法将在方法内部工作,但是如果在类执行期间被称为 身体。

Python还需要注意其他范围。理解会像函数一样创建局部作用域。 (它们基本上像生成器函数一样被编译,里面是__class__.tricks。)而且,捕获的异常仅限于它们的处理子句。

您可以使用内置的yield查看本地名称空间,而使用locals()查看全局名称空间。内置作用域只是内置模块。非本地人很棘手。如果编译器发现正在使用它们,它们实际上会显示在globals()中。函数对象在其locals()属性(即单元格的元组)中保留了对它们使用的非局部变量的引用。

答案 2 :(得分:0)

此示例有帮助吗?

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'

类定义就像运行一个函数。 foobar最初是全局值,但随后被重新分配,现在可作为类名称空间的一部分使用。

In [30]: x = AClass()                                                           
init: pre
class
instance
In [31]: x.foobar                                                               
Out[31]: 'instance'
In [32]: x.__class__.foobar                                                     
Out[32]: 'class'

定义实例时,foobar来自外部全局名称空间。 self.foobar首先访问类名称空间,但随后被重新定义为实例变量。

更改全局foobar反映在下一个实例创建中:

In [33]: foobar = 'post'                                                        
In [34]: y = AClass()                                                           
init: post
class
instance
In [35]: y.__class__.foobar                                                     
Out[35]: 'class'

您的教程页面9.3.1. Class Definition Syntax中说,在类定义中,有一个新的本地名称空间。这就是foobar='class'的定义。但是一旦脱离类定义,该名称空间便重命名为AClass

实例创建在类定义之外运行。因此,它“看到”了全局foobar,而不是本地的类。它必须指定名称空间。

定义一个类(或函数)和“运行”时作用域之间的区别可能会引起混淆。

相关问题