与函数相比,类的主体在定义时执行:
class A(object):
print 'hello'
输出:
hello
为什么会这样?它与@classmethod
/ @staticmethod
方法和类属性有关吗?
答案 0 :(得分:21)
一切在模块级别执行。函数体(和生成器表达式主体)在这里是异常,而不是规则。 Python执行所有操作以创建模块中包含的对象;就像Python中的所有内容一样,类是对象,函数也是如此。
类主体使用单独的代码对象的唯一原因是因为类主体在单独的命名空间中执行,然后该命名空间形成类属性。类主体不是唯一的这样的名称空间; set和dict comprehensions,在Python 3中,列表推导也使用单独的命名空间执行,并确定其本地人的范围。
因此函数和生成器表达式是例外,因为它们的整个目的将在以后执行。请注意,定义 功能已执行:
>>> import dis
>>> dis.dis(compile('def foo(): pass', '<stdin>', 'exec'))
1 0 LOAD_CONST 0 (<code object foo at 0x106aef2b0, file "<stdin>", line 1>)
3 MAKE_FUNCTION 0
6 STORE_NAME 0 (foo)
9 LOAD_CONST 1 (None)
12 RETURN_VALUE
MAKE_FUNCTION
字节码创建了函数对象,以及该函数的存储字节码,结果绑定到全局名称foo
。
类对象在这里没有什么不同; class
语句生成一个类对象,作为该对象的一部分,我们需要知道类体中的属性。
如果Python 不执行类主体,则其他代码无法使用这些类成员。您无法访问类属性(包括类方法和静态方法),您无法设置类属性等。
当然,任何属于类主体的函数都是而不是。就像顶级函数一样,只执行MAKE_FUNCTION
字节码,然后将生成的本地名称(使用STORE_FAST
设置)转换为类属性,类似于绑定到全局的全局函数对象与STORE_NAME
。
答案 1 :(得分:8)
根据Class definitions - Python documentation:
类定义是一个可执行语句。它首先评估 继承列表,如果存在。继承列表中的每个项目都应该 求值为允许子类化的类对象或类类型。的的 然后在新的执行框架中执行class的套件(参见章节 命名和绑定),使用新创建的本地命名空间和 原始全局命名空间。(通常,该套件仅包含功能 定义。)当类的套件完成执行时,执行它 帧被丢弃但保存了本地命名空间。一类 然后使用基类的继承列表创建对象 以及属性字典的已保存本地名称空间。班级 name绑定到原始本地名称空间中的此类对象。