Class 静态方法与 class 方法的变量作用域
Class variable scope for static vs class methods
我在 python class 变量上发现了一个奇怪的行为(至少对我来说很奇怪)。
class Base(object):
_var = 0
@classmethod
def inc_class(cls):
cls._var += 1
@staticmethod
def inc_static():
Base._var += 1
class A(Base):
pass
class B(Base):
pass
a = A()
b = B()
a.inc_class()
b.inc_class()
a.inc_static()
b.inc_static()
print(a._var)
print(b._var)
print(Base._var)
输出为1 1 2
。
这让我感到惊讶(我期待 4 4 4
),我想知道为什么?
当用 @classmethod
修饰时,cls
到 inc_class(cls)
的第一个参数是 class。 <class '__main__.A'>
和 <class '__main__.B'>
分别代表 A
和 B
。所以 cls._var
指的是 A
的 _var
,B
也是如此。在用 @staticmethod
修饰的 inc_static
中没有参数,您明确指的是 <class '__main__.Base'>
,一个不同的 _var
.
请注意 Base
和 A
的 __dict__
中的 '_var': 0
属性。 @classmethod
正在做您期望它做的事情,将成员绑定到 classes,在本例中为 A
和 B
.
>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 0, 'inc_class': <classmethod
object at 0x7f23037a8b38>, 'inc_static': <staticmethod object at
0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base' objects>,
'__weakref__': <attribute '__weakref__' of 'Base' objects>, '__doc__': None})
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})`
调用后Base.inc_static()
:
>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 1, 'inc_class':
<classmethod object at 0x7f23037a8b38>, 'inc_static': <staticmethod
object at 0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base'
objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>,
'__doc__': None})
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})
调用后A.inc_class()
:
>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 1, 'inc_class':
<classmethod object at 0x7f23037a8b38>, 'inc_static': <staticmethod
object at 0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base'
objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>,
'__doc__': None})
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, '_var': 1})
有趣的是 A
的 _var
是如何初始化的。请注意,在定义 cls._var
之前执行 cls._var += 1
。如 here, cls._var += 1
is equivalent to cls._var = cls._var; cls._var += 1
. Because of the way python does lookup 所述,第一次读取 cls._var
将在 A
中失败并继续在 Base
中找到它。在赋值时,_var
被添加到 A
的 __dict__
中,值为 Base._var
,然后一切正常。
>>> class Base(object):
... _var = 10
... @classmethod
... def inc_class(cls):
... cls._var += 1
...
>>> class A(Base):
... pass
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})
>>> A.inc_class()
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, '_var': 11})
尽管这两个 class 继承自 Base class,但它们是完全不同的对象。通过 a
和 b
的实例化,您有两个对象属于两个单独的 classes。当你打电话
a.inc_class()
b.inc_class()
您将 class A 的 _var
属性递增一次,然后对 class B 执行相同的操作。尽管它们共享相同的名称,但它们是不同的对象。如果您有 class A 的第二个实例,比如 a2
,并且您将再次调用该函数,那么这两个调用将操作同一个变量。这解释了如何获得前两个输出。
第三个输出是指 Base class 对象。同样,即使名称相同,它也是不同的对象。您将第三个对象递增两次,因此您得到 2
作为答案。
我在 python class 变量上发现了一个奇怪的行为(至少对我来说很奇怪)。
class Base(object):
_var = 0
@classmethod
def inc_class(cls):
cls._var += 1
@staticmethod
def inc_static():
Base._var += 1
class A(Base):
pass
class B(Base):
pass
a = A()
b = B()
a.inc_class()
b.inc_class()
a.inc_static()
b.inc_static()
print(a._var)
print(b._var)
print(Base._var)
输出为1 1 2
。
这让我感到惊讶(我期待 4 4 4
),我想知道为什么?
当用 @classmethod
修饰时,cls
到 inc_class(cls)
的第一个参数是 class。 <class '__main__.A'>
和 <class '__main__.B'>
分别代表 A
和 B
。所以 cls._var
指的是 A
的 _var
,B
也是如此。在用 @staticmethod
修饰的 inc_static
中没有参数,您明确指的是 <class '__main__.Base'>
,一个不同的 _var
.
请注意 Base
和 A
的 __dict__
中的 '_var': 0
属性。 @classmethod
正在做您期望它做的事情,将成员绑定到 classes,在本例中为 A
和 B
.
>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 0, 'inc_class': <classmethod
object at 0x7f23037a8b38>, 'inc_static': <staticmethod object at
0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base' objects>,
'__weakref__': <attribute '__weakref__' of 'Base' objects>, '__doc__': None})
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})`
调用后Base.inc_static()
:
>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 1, 'inc_class':
<classmethod object at 0x7f23037a8b38>, 'inc_static': <staticmethod
object at 0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base'
objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>,
'__doc__': None})
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})
调用后A.inc_class()
:
>>> Base.__dict__
mappingproxy({'__module__': '__main__', '_var': 1, 'inc_class':
<classmethod object at 0x7f23037a8b38>, 'inc_static': <staticmethod
object at 0x7f23037a8c18>, '__dict__': <attribute '__dict__' of 'Base'
objects>, '__weakref__': <attribute '__weakref__' of 'Base' objects>,
'__doc__': None})
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, '_var': 1})
有趣的是 A
的 _var
是如何初始化的。请注意,在定义 cls._var
之前执行 cls._var += 1
。如 here, cls._var += 1
is equivalent to cls._var = cls._var; cls._var += 1
. Because of the way python does lookup 所述,第一次读取 cls._var
将在 A
中失败并继续在 Base
中找到它。在赋值时,_var
被添加到 A
的 __dict__
中,值为 Base._var
,然后一切正常。
>>> class Base(object):
... _var = 10
... @classmethod
... def inc_class(cls):
... cls._var += 1
...
>>> class A(Base):
... pass
...
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None})
>>> A.inc_class()
>>> A.__dict__
mappingproxy({'__module__': '__main__', '__doc__': None, '_var': 11})
尽管这两个 class 继承自 Base class,但它们是完全不同的对象。通过 a
和 b
的实例化,您有两个对象属于两个单独的 classes。当你打电话
a.inc_class()
b.inc_class()
您将 class A 的 _var
属性递增一次,然后对 class B 执行相同的操作。尽管它们共享相同的名称,但它们是不同的对象。如果您有 class A 的第二个实例,比如 a2
,并且您将再次调用该函数,那么这两个调用将操作同一个变量。这解释了如何获得前两个输出。
第三个输出是指 Base class 对象。同样,即使名称相同,它也是不同的对象。您将第三个对象递增两次,因此您得到 2
作为答案。