• Python 笔记


    【python】 描述符:描述符协议、数据描述符、非数据描述符

    描述符-文字介绍

    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 元类 metaclass:


    """
    "Metaclasses" section example of metaclass that reveal order of all its
    methods called.

    """

    print(" >>> defining RevealingMeta(type)")


    class RevealingMeta(type):
    def __new__(mcs, name, bases, namespace, **kwargs):
    print(mcs, "__new__ called")
    return super().__new__(mcs, name, bases, namespace)

    @classmethod
    def __prepare__(mcs, name, bases, **kwargs):
    print(mcs, "__prepare__ called")
    return super().__prepare__(name, bases, **kwargs)

    def __init__(cls, name, bases, namespace, **kwargs):
    print(cls, "__init__ called")
    super().__init__(name, bases, namespace)

    def __call__(cls, *args, **kwargs):
    print(cls, "__call__ called")
    return super().__call__(*args, **kwargs)


    print(" >>> defining RevealingClass(metaclass=RevealingMeta)")


    class RevealingClass(metaclass=RevealingMeta):
    def __new__(cls):
    print(cls, "__new__ called")
    return super().__new__(cls)

    def __init__(self):
    print(self, "__init__ called")
    super().__init__()


    if __name__ == "__main__":
    print(" >>> Creating RevealingClass()")
    instance = RevealingClass()


    输出:
    '''
    >>> defining RevealingMeta(type)
    定义meta类时不调用 meta 类的函数

    >>> defining RevealingClass(metaclass=RevealingMeta)
    <class '__main__.RevealingMeta'> __prepare__ called
    <class '__main__.RevealingMeta'> __new__ called
    <class '__main__.RevealingClass'> __init__ called
    定义具体类时先调用 meta 类的 prepare 函数和 new 函数,再调用具体类的 init 方法
    prepare 函数和 new 函数,参数是 mcs,即meta类RevealingMeta
    init 函数和 call 函数,参数是 cls,即"具体类"RevealingClass

    >>> Creating RevealingClass()
    <class '__main__.RevealingClass'> __call__ called --> 调用meta 类的call函数,但参数是 cls,即"具体类"RevealingClass
    <class '__main__.RevealingClass'> __new__ called --> 参数是 cls
    <__main__.RevealingClass object at 0x10bea0bd0> __init__ called --> 参数是 self

    '''







  • 相关阅读:
    MongoDB中_id(ObjectId)生成
    springMVC 拦截器源码解析
    初识设计模式(代理模式)
    vue VNode如何使用,是什么东西?
    AOP 怎么理解?
    Java集合框架——jdk 1.8 ArrayList 源码解析
    System.arraycopy 怎么使用的?
    Java集合框架——容器的快速报错机制 fail-fast 是什么?
    maven 如何依赖工程项目里面的 jar 包
    IDEA maven 项目如何上传到私服仓库
  • 原文地址:https://www.cnblogs.com/cx2016/p/12251512.html
Copyright © 2020-2023  润新知