• 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)
  • 相关阅读:
    SQL中的选择判断
    Rsync
    LAMP性能优化的一些建议
    Toad9.7与Oracle11g在X86的Win7下的情况
    IIS中IUSR_和IWAM_:计算机名帐户的用户名和密码的用途
    winform编程中的跨线程访问资源(转)
    MSTDC服务的应用及相关错误的解决方案(转载)
    SQL Server 错误代码详解
    poj 1777梅森素数
    hdu 2815 baby_step c可为非素数
  • 原文地址:https://www.cnblogs.com/rprp789/p/9691505.html
Copyright © 2020-2023  润新知