1. 一切皆对象
在python中不仅仅是类的实例是对象,包括类和函数也是对象。
2.函数对象
可以看看下面的outer 返回的是什么
def outer():
def inner():
return 10
return inner
func = outer()
print(func)
可以看出,func为一个函数对象:<function outer.<locals>.inner at 0x0000021BAFB4C620>。
并且这个函数对象是可以调用的。
def outer():
def inner():
return 10
return inner
func = outer()
print(func())
这个打印的结果为10。
我在这里觉得函数的调用是分三步的,
- 定义一个函数func对象
- 发现一个func对象
- 调用这个func()对象
3.descriptor
一个类中定义了__set__, __get__和__delete__中的一个或多个就是descriptor。
其中__get__函数定义的时候需要的参数为三个,分别为self, instance, owner。
__set__函数定义的时候需要的参数也为三个,分别是self, instance, value。
可以通过一个例子来看看这三个参数分别是什么。
class A:
def __init__(self, val):
self.val = val
def __get__(self, instance, owner):
print('self is ', self)
print('instance is ', instance)
print('owner is ', owner)
return self.val
class B:
a = A(10)
b = B()
print(b.a)
打印的结果为:
self is <__main__.A object at 0x000001AACC2FCDD8>
instance is <__main__.B object at 0x000001AACC4A1438>
owner is <class '__main__.B'>
10
可以知道,self很简单,就是descriptor的实例;instance就是descriptor所在类中的实例;owner指的是descriptor所在的类。
还可以看出来的是,打印的b.a不是一个是实例对象,而是通过调用a.__get__()函数返回的值。
所以我感觉这个descriptor叫做描述器(借鉴于decorator)似乎更合适。。。
因为这个descriptor似乎是用来描述所在的实例的。。。
总结:通过实例调用(b.a)这个descriptor对象,返回的并不是这个descriptor(你可以尝试打印一个实例对象,看看返回的是什么),而是该descriptor调用__get__返回的值,所以这个a像是b的描述器。
4.一个descriptor用来做decorator的实例。
class LazyProperty(object):
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
else:
value = self.func(instance)
setattr(instance, self.func.__name__, value)
return value
class A:
@LazyProperty
def func(self):
time.sleep(1)
return 10
看见装饰器,刚开始没习惯可能会有点困扰。其实没那么复杂,根据定义一步步解读就好了。
类A中的func被LazyProperty所装饰,那么其实它可以简单的转化为两步
class A:
def func(self):
time.sleep(1)
return 10
func = LazyProperty(func)
于是,这个func是不是就是转化成为了一个descriptor呢?
对的,你没猜错,这个func就是A的描述器!!!
那么通过如下调用返回的结果是什么呢???
a = A()
print(a.func)
当然是这个func(需要注意的是,这个func并不是类A中定义的func函数,而是LazyProperty的实例)通过调用__get__返回的值呀!
由于是通过A的实例a来取得其中的func的值,所以__get__中的参数instance并不是None,而是a(那么什么时候这个instance为None呢?那就是通过类来调用这个属性,也就是A.func)。
所以会通过setattr函数为实例a添加属性func.__name__,并对其赋值为调用类A中定义的func函数返回的值。
那么,之后再次调用a.func返回的是什么呢?
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 = self.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)
print(a.func)
可以看出来的是两个打印的值在程序运行1秒之后是几乎同时出现的, 第一次打印的a.func是sleep(1)后返回的结果。而第二次基本上和第一次打印a.func同时出现。
为什么会不是等第一个出现需要1秒钟,等第二个出现再需要1秒钟呢?怎么两个几乎同时出现呢?
这个当时困扰我很久,,,甚至懵逼很久。
但是是相当简单的,第二次就是应该直接返回的数值10。
因为实例a的属性func,变成了数值10。是通过setattr()来实现的。
可以等价的换为如下问题:
下面第二次打印a的值是什么呢?
a = 10
print(a)
a = 11
print(a)
这个问题很简单了把,当然是11啦!!!
在Django框架中,也用到了同样的方式,在django.utils.functional
代码如下:
class cached_property:
"""
Decorator that converts a method with a single self argument into a
property cached on the instance.
Optional ``name`` argument allows you to make cached properties of other
methods. (e.g. url = cached_property(get_absolute_url, name='url') )
"""
def __init__(self, func, name=None):
self.func = func
self.__doc__ = getattr(func, '__doc__')
self.name = name or func.__name__
def __get__(self, instance, cls=None):
"""
Call the function and put the return value in instance.__dict__ so that
subsequent attribute access on the instance returns the cached value
instead of calling cached_property.__get__().
"""
if instance is None:
return self
res = instance.__dict__[self.name] = self.func(instance)
return res
给大家留个问题思考
1. __get__函数中的获取value值可不可以通过owner.func(instance)来获得呢?