在我的印象中,类中的函数是可以有两种调用方式的,如下:
class B:
def func(self):
return 10
b = B()
b.func()
B.func(b)
于是,在之前研究descriptor的时候,我就有了几点困惑
我将代码更换成了如下两个版本
版本1
import time
class LazyProperty(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
else:
value = owner.func(instance)
setattr(instance, self.func.__name__, value)
return value
class A:
@LazyProperty
def func(self):
time.sleep(1)
return 10
a = A()
print(a.func)
这个版本出现的错误是:TypeError: 'LazyProperty' object is not callable。
为什么会出现这个问题呢?
由于instance不为None,所以会调用owner.func(instance)。
也就是会调用A.func(a),但是呢A.func并不是一个函数,而是一个LazyProperty的实例。
并且这个实例没有实现__call__方法,是不能像函数一样调用的。
版本2
import time
class LazyProperty(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
value = owner.func(instance)
setattr(instance, self.func.__name__, value)
return value
class A:
@LazyProperty
def func(self):
time.sleep(1)
return 10
a = A()
print(a.func)
表面上一看,这个应该和上个版本出现一样的问题。
但是并不是和之前出现的是一样的错误。
这次出现的错误是:RecursionError: maximum recursion depth exceeded
这个是为什么呢?
还记得我之前说的函数的调用是分为三步的吗?
定义不用说了,现在就谈谈后面两步。
owner.func(instance)
在上面代码中的含义是不是A.func(a)?
后面两步中的第一步就是找到这个A.func对象。
记不记得A中的func其实是一个descriptor,并不是一个函数对象。
所以A.func会返回什么呢?
会返回descriptor中的__get__()返回的对象
但是这个这个__get__()函数中的第一步是什么?
对的,你没看错,还是这个owner.func(instance)。
所以,还会继续寻找owner.func这个对象。
有么有发现这是一个坑,会无限跳转下去?
于是就有了错误,RecursionError: maximum recursion depth exceeded。
终结,想要获取value值,不能通过别的方式。
其实,还有一个问题,那就是为什么可以通过self.func(instance)来实现呢?
想要调用类中的一个函数不是只有开头说的两个方式的吗?
错了,错了。
还是有第三种方式的。
那就是在类中就调用,这个就和作用域一样了。
在一个类中是可以直接调用其函数的。
func = LazyProperty(func)
其实可以换一种理解方式.
lazyProperty = LazyProperty(func)
要获取A.lazyProperty,就要通过__get__函数。
然而,这个lazyProperty是不是在类A的内部。
是不是就可以直接调用类A中定义的func方法。
于是就是lazyProperty.func(instance),得出了所想要的结果了呢!