Python 描述符

作者: Alcazar | 来源:发表于2019-06-20 22:21 被阅读28次
描述符-文字介绍

Python为开发者提供了一个非常强大的功能——描述符。那什么是描述符呢?通过查看Python的官方文档,我们知道把实现了__get__()、__set__()和__delete__()中的其中任意一种方法的类称之为描述符,描述符的本质是新式类,并且被代理的类(即应用描述符的类)也是新式类。描述符的作用是用来代理一个类的属性,需要注意的是描述符不能定义在类的构造函数中,只能定义为类的属性,它只属于类的,不属于实例,我们通过查看实例和类的字典即可知晓。

描述符是可以实现大部分Python类特性中最底层的数据结构的实现手段,我们常使用的@classmethod、@staticmethd、@property、甚至是slots等属性都是通过描述符来实现的。它是很多高级库和框架的重要工具之一,是使用到装饰器或者元类的大型框架中的一个非常重要组件。在一般的开发中我们可能用不到描述符,但是我们如果想要开发一个大型的框架或者大型的系统,那使用描述符会起到如虎添翼的作用。它的加盟将会使得系统更加完美。

述符协议:

实现了 get()、set()、delete() 其中至少一个方法的类,就是一个描述符。

  • __get__:用于访问属性。它返回属性的值,若属性不存在、不合法等都可以抛出对应的异常。
  • __ set__:在属性分配操作中调用。不会返回任何内容。
  • __ delete__:制删除操作。不会返回内容。

描述符的特点

  • 是一个类,定义了访问另一个类属性的方式;

  • 把至少实现了内置属性set()和get()方法的描述符称为数据描述符;

  • 把实现了除set()以外的方法的描述符称为非数据描述符;

  • 访问对象属性的时候:obj.attr,一般会先调用__getattribute__()方法,查找顺序如下:
    (1)数据描述符 (2) __dict__属性 (3)非数据描述符

  • 描述符的优先级的高低如下:

类属性 > 数据描述符 > 实例属性 > 非数据描述符 > 找不到的属性触发_getattr__()


数据描述符

举例来看:

class RevealAccess:
    def __init__(self,initval = None,name='var'):
        self.val = initval
        self.name = name

    def __get__(self, instance, owner):
        print("获取..",self.name)
        return self.val

    def __set__(self, instance, value):
        print("设置值:",self.name)
        self.val = value

class MyClass:
    x = RevealAccess(10,"var 'x'")
    y = 5

    def __init__(self):
       self.x = 'self x'
        # pass
m = MyClass()
print(m.x)
print("-----------------")
m1 = MyClass()
m1.x = 20
print(m1.x)

output:

设置值: var 'x'
获取.. var 'x'
self x
-----------------
设置值: var 'x'
设置值: var 'x'
获取.. var 'x'
20

分析一下内存:



非数据描述符

class InitOnAccess:
    def __init__(self, klass, *args, **kwargs):
        self.klass = klass
        self.args = args
        self.kwargs = kwargs
        self._initialized = None

    def __get__(self, instance, owner):
        if self._initialized is None:
            print("_initialized is None")
            self._initialized = self.klass(*self.args, **self.kwargs)
        else :
            print("cached")

        return self._initialized

class TestClass:
    lazy_initialized =InitOnAccess(list, "arguments")


t = TestClass()
t.lazy_initialized
t.lazy_initialized

output:

_initialized is None
cached

相关文章

网友评论

    本文标题:Python 描述符

    本文链接:https://www.haomeiwen.com/subject/fyfdqctx.html