Python中@staticmethod和@classmethod的区别是什么?
用装饰器标记的方法@staticmethod
和用@classmethod
标记的方法有什么区别呢?
36 个回答
简单来说,@classmethod
是用来定义一个方法的,这个方法的第一个参数是调用它的类,而不是类的实例。换句话说,它可以直接访问类本身。而 @staticmethod
则没有任何隐含的参数,也就是说,它不需要知道是哪个类在调用它。
静态方法是一种方法,它对被调用的类或实例一无所知。它只接收传入的参数,没有隐含的第一个参数。
而类方法则不同,它会接收到被调用的类,或者是被调用实例的类,作为第一个参数。这在你想让这个方法成为类的工厂时非常有用:因为它会把实际被调用的类作为第一个参数传入,所以即使涉及到子类,你也总是可以实例化正确的类。例如,dict.fromkeys()
这个类方法,当在子类上调用时,会返回子类的实例:
>>> class DictSubclass(dict):
... def __repr__(self):
... return "DictSubclass"
...
>>> dict.fromkeys("abc")
{'a': None, 'c': None, 'b': None}
>>> DictSubclass.fromkeys("abc")
DictSubclass
>>>
也许一些示例代码会更有帮助:注意一下 foo
、class_foo
和 static_foo
的调用方式有什么不同:
class A(object):
def foo(self, x):
print(f"executing foo({self}, {x})")
@classmethod
def class_foo(cls, x):
print(f"executing class_foo({cls}, {x})")
@staticmethod
def static_foo(x):
print(f"executing static_foo({x})")
a = A()
下面是一个对象实例调用方法的常见方式。对象实例 a
会自动作为第一个参数传入。
a.foo(1)
# executing foo(<__main__.A object at 0xb7dbef0c>, 1)
对于类方法(classmethod)来说,传入的第一个参数是对象实例的类,而不是 self
。
a.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)
你也可以通过类来调用 class_foo
。实际上,如果你把某个方法定义为类方法,通常是因为你打算从类中调用它,而不是从类的实例中调用。比如 A.foo(1)
会报错,但 A.class_foo(1)
就可以正常工作:
A.class_foo(1)
# executing class_foo(<class '__main__.A'>, 1)
人们发现类方法的一个用途是创建可继承的替代构造函数。
对于静态方法(staticmethod)来说,既没有 self
(对象实例),也没有 cls
(类)作为第一个参数自动传入。它们的行为就像普通函数,只不过你可以从实例或类中调用它们:
a.static_foo(1)
# executing static_foo(1)
A.static_foo('hi')
# executing static_foo(hi)
静态方法用于将与类有某种逻辑关联的函数归类到这个类中。
foo
只是一个函数,但当你调用 a.foo
时,你得到的不是单纯的函数,而是一个“部分应用”的版本,其中对象实例 a
被绑定为函数的第一个参数。foo
需要两个参数,而 a.foo
只需要一个参数。
a
被绑定到 foo
上。这就是下面所说的“绑定”:
print(a.foo)
# <bound method A.foo of <__main__.A object at 0xb7d52f0c>>
在 a.class_foo
中,a
并没有绑定到 class_foo
,而是类 A
被绑定到 class_foo
。
print(a.class_foo)
# <bound method type.class_foo of <class '__main__.A'>>
在静态方法中,尽管它是一个方法,a.static_foo
只是返回一个没有任何参数绑定的普通函数。static_foo
需要一个参数,而 a.static_foo
也需要一个参数。
print(a.static_foo)
# <function static_foo at 0xb7d479cc>
当然,当你用类 A
来调用 static_foo
时,情况也是一样的。
print(A.static_foo)
# <function static_foo at 0xb7d479cc>