zoukankan      html  css  js  c++  java
  • chapter9.5、描述器

    描述器

    描述器的表现

    用到三个魔术方法,__get__,  __set__,  __delete__

    方法签名如下

    object.__get__(self,instance,owner)

    object.__set__(self,instance,value)

    object.__delete__(self,instance)

      self    指代当前实例,调用者

      instance  是owner的实例

      owner    是属性的所属的类

    class A:
        def __init__(self):
            self.a1 = 'a1'
            print('A.init')class B:
        x = A()
        def __init__(self):
            print('B.init')
    
    print('_'*20)
    print(B.x.a1)
    
    print('~~~~~~~~~')
    b = B()
    print(b.x.a1)
    #运行结果

    A.init
    ____________________
    a1
    ~~~~~~~~~
    B.init
    a1

    可以看出执行的先后顺序,类加载的时候,类变量需要先生成,而类B的属性是类A的实例,所以A先初始化,打印A.init,

    再执行调类的属性也就是A的实例的属性,打印a1

    实例化B并初始化,打印B.init

    打印b.x.a1,会查找类属性b.x,指向A的实例,所以返回A实例的属性a1的值

    接着对类A中实现__get__方法,

    class A:
        def __init__(self):
            self.a1 = 'a1'
            print('A.init')
    
        def __get__(self, instance, owner):
            print('A.__get',self,instance,owner)
            return self
    
    class B:
        x = A()
        def __init__(self):
            print('B.init')
    
    print('_'*20)
    print(B.x.a1)
    
    print('~~~~~~~~~')
    b = B()
    print(b.x.a1)

    因为定义了__get__方法,类A就是一个描述器,对类B或者类B的实例的属性获取,成为对类A的实例的访问,就会调用__get__方法,get方法都是去取值的,返回值就是A的实例,实例有a1 的属性,返回正常。

    测试一下,B的实例并不能触发__get__

    描述器的定义

    Python中,一个类实现了__get__,__set__,__delete__三个方法中任何一个方法,就是描述器

    如果仅实现了__get__,就是非数据描述器  non-data descriptor;

    如果同时实现了__get__,__set__就是数据描述符。

    如果一个类的类属性设置为描述器实例,那么它被称为owner属主

    属性的访问顺序

    实例的__dict__优先于非数据描述器

    数据描述器优先于实例的__dict__

    __delete__方法有同样的效果,有了这个方法,也是数据描述器

    b.x = 500   对象可用,调用了数据描述器的__set__方法,或调用非数据描述器的实例覆盖

    B.x = 600  赋值即定义,这是覆盖类属性。

    python中的描述器

    描述器在Python中应用十分广泛,

    python的staticmethod和classmethod都是实现为非数据描述器

    因此,实例可以重新定义和覆盖方法,这允许单个实例获取与同一类其他实例的不同的行为

    staticmethod的简易实现:

    class StaticMethod:
        def __init__(self,fn):
            self.foo = fn
    
        def __get__(self, instance, owner):
            return self.foo
    class B:
        @StaticMethod
        def foo():
            print('static method')
    
    B.foo()
    B().foo()

    classmethod简易实现

    import functools
    class ClassMethod:
        def __init__(self,fn):
            self.foo = fn
    
        def __get__(self, instance, owner):
            return functools.partial(self.foo,owner)###如果直接返回self.foo(owner),就是返回这个函数的执行结果,也就是返回None,None()无法执行
       
    class G:
        @ClassMethod###foo(cls) = ClassMethod(foo)(cls)
        def foo(cls):
            print(cls.__name__)
    
    G.foo()

    描述器实现数据检查,使用inspect模块。

    import inspect
    class LoggerCheck:
    
        def __init__(self,name,type):
            self.name = name
            self.type = type
    
        def __get__(self, instance, owner):
            if instance is not None:
                return instance.__dict__[self.name]
            return self
    
        def __set__(self, instance, value):
            if not isinstance(value,self.type):
                raise TypeError
            instance.__dict__[self.name] = value
    
    def typeassert(cls):
        params = inspect.signature(cls).parameters
        for name,type in params.items():
            if type.annotation != inspect._empty:
                setattr(cls,name,LoggerCheck(name,type.annotation))##在类中注入属性,指向数据检查的实例LoggerCheck
        return cls
    
    @typeassert  ##Person = typeasser(Person)
    class Person:
        def __init__(self,name:str,age:int):
            self.name = name
            self.age = age
    tom = Person('tom',15)

    以上的装饰器可以改为类装饰器

    import inspect
    class LoggerCheck:
    
        def __init__(self,name,type):
            self.name = name
            self.type = type
    
        def __get__(self, instance, owner):
            if instance is not None:
                return instance.__dict__[self.name]
            return self
    
        def __set__(self, instance, value):
            if not isinstance(value,self.type):
                raise TypeError
            instance.__dict__[self.name] = value
    
    class TypeAssert:
        def __init__(self,cls):
            params = inspect.signature(cls).parameters
            for name, type in params.items():
                if type.annotation != inspect._empty:
                    setattr(cls, name, LoggerCheck(name, type.annotation))
            self.cls = cls
    
        def __call__(self, name,age):
            return self.cls(name,age)
    
    @TypeAssert  ##Person = TypeAsser(Person)
    class Person:
        def __init__(self,name:str,age:int):
            self.name = name
            self.age = age
    tom = Person('tom',15)
  • 相关阅读:
    windows7 下 apache2.4 和 php5.5 及 mysql5.6 的安装与配置
    JavaScript高级编程 (1)
    关于解决乱码问题的一点探索之二(涉及Unicode(utf-16)和GBK)
    关于解决乱码问题的一点探索之一(涉及utf-8和GBK)
    Windows上安装、配置MySQL的常见问题
    解析DXF图形文件格式
    vue三十一:vue配置反向代理
    vue三十:eslint修复错误和打包用于生产
    vue二十九:多个文件组件集成
    vue二十八:Vue-Cli之环境搭建之node安装脚手架和使用脚手架创建vue项目
  • 原文地址:https://www.cnblogs.com/rprp789/p/9691505.html
Copyright © 2011-2022 走看看