在学习的过程中偶然发现classmethod常常与property连用的情况,但是不理解底层的逻辑,以下是我总结过后的一些简单应用
property的应用(定制延迟计算)
因为对象属性执行的优先级是数据描述符(定义了__get__方法而且至少有有__delete__,__set__方法中的任何一个)>实例对象>非数据描述符(只定义了__get__方法)>类对象,所以计算了一次area之后,会将计算的值res保存在实例对象的属性中,因为实例对象>非数据描述符,计算了一次之后,再调用返回的都是r3实例的area实例属性,使得计算的方法area只调用了一次,可以节省一定的运行速度
classmethod,以下给出了类似官方代码的逻辑
>>> class ClassMethod:
def __init__(self,f):
self.f = f
def __get__(self,obj,cls=None):
if cls is None:
print('旺财')
cls = type(obj)
if hasattr(type(self.f),'__get__'):
print(f'来福,{type(self.f)}')
print(cls)
return self.f.__get__(cls,cls)
return 0
>>> class D:
@ClassMethod
@property
def __doc__(cls):
return f"class is::{cls.__name__}"
>>> d = D()
>>> d.__doc__
来福,<class 'property'>
<class '__main__.D'>
'class is::D'
可见,先将类D中的__doc__方法变成property描述符,再将该描述符当作函数参数传入classmethod中,首先我们要思考,为什么classmethod能真正将cls参数变成类对象呢?它只是把self改成了cls,就改了个名称是怎么做到的?
答案是在ClassMethod类中的if语句if cls is None(我也不理解为什么要将参数cls默认为None,设不设置cls参数都会是类D这个类对象),最后将类D赋值给cls变量,使得在下一行if语句中,输出self.f.__get__(cls,cls),因为这里的self.f是property对象,把cls参数传给了property对象__get__中第一个(这个参数并没有发生作用,只是一个占位)和第二个参数(这个参数才是把cls类对象传给了__doc__方法中的参数cls),所以发生参数改变的原因就是在ClassMethod方法中实现的
下面是property和classmethod的简单运用
通过自定义描述符记录该实例对象调用某个函数的次数
# 测量该类的对象该函数调用的次数
class Class_Property:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
instance.func1_count += 1
return self.func(instance)
class C:
def __init__(self):
self.func1_count = 0
@Class_Property
def func1(self):
print('func1')
c = C()
c.func1
c.func1
c.func1
print(c.func1_count)
# 运行结果
func1
func1
func1
3
通过添加classmethod,计算该类的某个函数被对象调用的次数
# 测量该类的某个函数被调用的次数
class Class_Property:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
instance.func1_count += 1
return self.func(instance)
class C:
func1_count = 0
@classmethod
@Class_Property
def func1(cls):
print('func1')
c = C()
c.func1
c.func1
c.func1
print(C.func1_count)
# 运行结果
func1
func1
func1
3