Python中的descriptor

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。

    我在这里觉得函数的调用是分三步的,

  1. 定义一个函数func对象
  2. 发现一个func对象
  3. 调用这个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)来获得呢?

猜你喜欢

转载自blog.csdn.net/hsc_1/article/details/81027546