看了这么多,没看到关于这两个@classmethod 和 @staticmethod 实现原理的,找了一些资料,写一下我现在的理解。
第一部分:classmethod前面的@符号的含义,装饰器
首先这个@符号表示的是语法糖(装饰器),解释可以看看这个Python中语法糖及带参语法糖 - PersistencePythoner - 博客园www.cnblogs.com
简单来讲就是:
def my_decorator(func):
def wrapper():
func()
pass
return wrapper
# 写法一
@my_decorator
def foo():
pass
# 写法二
foo = my_decorator(foo)
上面的代码,@my_decorator等价于foo = my_decorator(foo),foo是函数句柄
第二部分:什么是描述符
简单来说,如果一个类实现了__ get __ , __ set__, __delete __ 方法的一个或多个,就称这个为描述符。用VS Code打开@staticmethod和@classmethod的代码,可以发现两个类都实现了get方法。而一般情况下,实例化一个对象会触发_init_方法,参数f为Callable类型的对象
取值操作会触发 __get_ 方法
第三部分:@classmethod和@staticmethod的原理
@classmethod和@staticmethod实际上就是描述符和装饰器的组合,为了更好的说明他们的原理,我们可以自己实现一下,以@classmethod为例
class myclassmethod(object):
def __init__(self, method=None):
self.method = method
def __get__(self, instance, cls):
return lambda *args,**ks:self.method(cls,*args,**ks)
# 断点一
class MyClass(object):
@myclassmethod
def func_1(cls,*args,**ks):
print(cls)
print(args)
print(ks)
# 断点二
MyClass.func_1(123,age=199)
用VS Code调试,第一个断点的时候,执行的操作就是装饰器的工作,等价于
MyClass.func_1 = myclasmethod(func_1)
第二个断点,修饰符发挥作用,调用函数,__get__被触发,cls被赋值为拥有者类,也就是其他答案里常常说到的cls默认参数。
最后的输出结果实际上如下:
# print(cls)
(123,) # print(args)
{'age': 199} # print(ks)
参考:location-of-classmethodstackoverflow.com
class myclassmethod(object):
def __init__(self, method=None):
self.method = method
def __get__(self, instance, cls):
return lambda *args,**ks:self.method(cls,*args,**ks)
class mystaticmethod(object):
def __init__(self,method):
self.method = method
def __get__(self,instance,cls):
return lambda *args,**ks:self.method(*args,**ks)
class MyClass(object):
@myclassmethod
def func_1(cls,*args,**ks):
print(cls)
print(args)
print(ks)
@mystaticmethod
def func_2(age):
print(age)
def func_3(self):
MyClass.func_2(6)
MyClass.func_1(123,age=199)
MyClass.func_2(121213)
obj = MyClass()
obj.func_3()