# classmethod和staticmethod
# @classmethod
# 概述
@classmethod是一个装饰器,用来指定一个类方法。类方法是类所拥有的方法,而不是类的实例所拥有的方法。类方法的第一个参数是一个指向类的指针,一般用cls作为参数名。
使用@classmethod装饰器定义的类方法,可以在类和类的实例中被调用,如下所示:
class MyClass:
x = 0
@classmethod
def get_x(cls):
return cls.x
@classmethod
def set_x(cls, val):
cls.x = val
print(MyClass.get_x()) # 0
MyClass.set_x(1)
print(MyClass.get_x()) # 1
my_instance = MyClass()
print(my_instance.get_x()) # 1
从上面的代码可以看出,@classmethod定义的方法可以直接通过类名进行调用,也可以通过实例进行调用,因为实例也是类的一种。另外,通过cls参数可以访问和修改类的属性。
# @staticmethod
# 概述
关于staticmethod:
staticmethod的核心其实是“是不是需要建立这个类的instance”。如果希望在不建立instance的情况下调用函数,一般就是用staticmethod。如果永远是instance调用,其实就写成普通的method就好。
@staticmethod是一个装饰器,用来指定一个静态方法。静态方法是类中的函数,不依赖于类或实例,而是在其所在的模块中定义。因此,静态方法的第一个参数没有特殊的含义,可以任意指定。
使用@staticmethod装饰器定义的静态方法,可以在类和类的实例中被调用,如下所示:
class MyClass:
@staticmethod
def add(x, y):
return x + y
print(MyClass.add(1, 2)) # 3
my_instance = MyClass()
print(my_instance.add(1, 2)) # 3
从上面的代码可以看出,静态方法和普通函数很相似,不依赖于类和实例,因此也可以直接通过类名进行调用。
# 源码层面
首先观察下面的代码以及执行结果:
def decorator(func):
def inner(*args,**kwargs):
print("inner",locals())
args[0].ALL = 10086
func(*args,**kwargs)
return inner
class A:
@classmethod
@decorator
def decdclsfun(*args,**kwargs):
print(args[0].ALL)
print("clsfun", " ", locals())
@staticmethod
def stafun(*args,**kwargs):
print("stafun", " ", locals())
def insfun(*args,**kwargs):
print("insfun", " ", locals())
if len(args):
sm = args[0]
print(sm,type(sm))
@classmethod
def clsmethod(*args,**kwargs):
print("cleanclsmethod", " ", locals())
A.decdclsfun() # inner {'args': (), 'kwargs': {}, 'func': <function A.clsfun at 0x000001B621C46048>}
# 10086
# decdclsfun {'args': (<class '__main__.A'>,), 'kwargs': {}}
A.stafun() # stafun {'args': (), 'kwargs': {}}
A.insfun() # insfun {'args': (), 'kwargs': {}}
A.clsmethod() # clsmethod {'args': (<class '__main__.A'>,), 'kwargs': {}}
a = A()
a.decdclsfun() # inner {'args': (), 'kwargs': {}, 'func': <function A.clsfun at 0x000001B621C46048>}
# 10086
# clsfun {'args': (<class '__main__.A'>,), 'kwargs': {}}
a.stafun() # stafun {'args': (), 'kwargs': {}}
a.insfun() # insfun {'args': (<__main__.A object at 0x000001F7515BC248>,), 'kwargs': {}}
# <__main__.A object at 0x000001F7515BC248> <class '__main__.A'>
a.clsmethod() # clsmethod {'args': (<class '__main__.A'>,), 'kwargs': {}}
仔细观察可以看出,实际上Python是不分实例和类这种概念的-类可以调实例方法,实例也可以调类方法。
当实例调用方法时实例会将自己作为第一个参数传递进去,而classmethod和staticmethod实际上都是在实例方法的基础之上通过装饰器实现的。
classmethod大致逻辑如下:
-
klass 在这里是一个参数,代表类本身。当类调用一个类方法时,这个参数会被自动填充为这个类的类型。如果这个类方法是通过实例进行调用的,那么这个参数会被填充为这个实例的类型。通过这个参数,我们可以在类方法中访问类本身以及类级别的属性和方法。 - 至于为什么是类还不是实现的
__call__方法则和描述器相关。
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def newfunc(*args):
return self.f(klass, *args)
return newfunc
staticmethod同理,只是直接调用被修饰的函数。
← `@`符号 property装饰器 →