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)

猜你喜欢

转载自www.cnblogs.com/rprp789/p/9691505.html
9.5