Descriptor (__get __, __ set __, __ delete__)

103- descriptor (get, set, delete) - Warning .gif

Descriptor

  • What descriptors are: descriptor is essentially a new class, in this new class, at least to achieve a __get __ (), __ set __ (), __ a () in the delete __, which is also called descriptors agreement

    • __get __ (): When you call a property trigger

    • __set __ (): When the assignment is a property trigger

    • __delete __ (): When using del to delete attributes, trigger

  • Definition of a descriptor

class Foo:  # 在python3中Foo是新式类,它实现了__get__(),__set__(),__delete__()中的一个三种方法的一个,这个类就被称作一个描述符
    def __get__(self, instance, owner):
        pass

    def __set__(self, instance, value):
        pass

    def __delete__(self, instance):
        pass

Action descriptor

  • Descriptor is doing: descriptor additional agent is used to effect a class attribute of the descriptor must be defined as a property of that class can not be defined to the constructor
class Foo:
    def __get__(self, instance, owner):
        print('触发get')

    def __set__(self, instance, value):
        print('触发set')

    def __delete__(self, instance):
        print('触发delete')


f1 = Foo()
  • These three new class contains a method called descriptors, attributes call / assignments / deleted by the generated instance of this class, does not trigger these three methods
f1.name = 'nick'
f1.name
del f1.name

When, where, it will trigger the execution of these three methods

class Str:
    """描述符Str"""

    def __get__(self, instance, owner):
        print('Str调用')

    def __set__(self, instance, value):
        print('Str设置...')

    def __delete__(self, instance):
        print('Str删除...')


class Int:
    """描述符Int"""

    def __get__(self, instance, owner):
        print('Int调用')

    def __set__(self, instance, value):
        print('Int设置...')

    def __delete__(self, instance):
        print('Int删除...')


class People:
    name = Str()
    age = Int()

    def __init__(self, name, age):  # name被Str类代理,age被Int类代理
        self.name = name
        self.age = age


# 何地?:定义成另外一个类的类属性

# 何时?:且看下列演示

p1 = People('alex', 18)
Str设置...
Int设置...
  • Use of descriptors Str
p1.name
p1.name = 'nick'
del p1.name
Str调用
Str设置...
Str删除...
  • Use of descriptors Int
p1.age
p1.age = 18
del p1.age
Int调用
Int设置...
Int删除...
  • Chou Chou us what happened in the end
print(p1.__dict__)
print(People.__dict__)
{}
{'__module__': '__main__', 'name': <__main__.Str object at 0x107a86940>, 'age': <__main__.Int object at 0x107a863c8>, '__init__': <function People.__init__ at 0x107ba2ae8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
  • supplement
print(type(p1) == People)  # type(obj)其实是查看obj是由哪个类实例化来的
print(type(p1).__dict__ == People.__dict__)
True
True

Two kinds of descriptor

Data Descriptor

  • At least achieved __get __ () and __set __ ()
class Foo:
    def __set__(self, instance, value):
        print('set')

    def __get__(self, instance, owner):
        print('get')

Non-data descriptor

  • Did not realize __set __ ()
class Foo:
    def __get__(self, instance, owner):
        print('get')

Descriptor Notes

103- descriptor (get, set, delete) - Note .jpg

  1. Descriptor itself should be defined as the new class, the proxy class should be new class

  2. Descriptor must be defined as a property of that class, and can not be defined to the constructor

  3. To strictly follow this priority, the priority from high in the end were

    1. Class Properties

    2. Data Descriptor

    3. Instance Properties

    4. A non-data descriptor

    5. Can not find the property trigger __getattr __ ()

Use Descriptor

  • As we all know, python is a weakly typed language, that is, no restrictions on the type of assignment parameters, let's limit the functionality through the implementation type descriptor mechanism

Chopper small scale

class Str:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str('name')

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p1 = People('nick', 18, 3231.3)
set---> <__main__.People object at 0x107a86198> nick
  • transfer
print(p1.__dict__)
{'name': 'nick', 'age': 18, 'salary': 3231.3}
print(p1.name)
get---> <__main__.People object at 0x107a86198> <class '__main__.People'>
nick
  • Assignment
print(p1.__dict__)
{'name': 'nick', 'age': 18, 'salary': 3231.3}
p1.name = 'nicklin'
print(p1.__dict__)
set---> <__main__.People object at 0x107a86198> nicklin
{'name': 'nicklin', 'age': 18, 'salary': 3231.3}
  • delete
print(p1.__dict__)
{'name': 'nicklin', 'age': 18, 'salary': 3231.3}
del p1.name
print(p1.__dict__)
delete---> <__main__.People object at 0x107a86198>
{'age': 18, 'salary': 3231.3}

His help

class Str:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str('name')

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


# 疑问:如果我用类名去操作属性呢
try:
    People.name  # 报错,错误的根源在于类去操作属性时,会把None传给instance
except Exception as e:
    print(e)
get---> None <class '__main__.People'>
'NoneType' object has no attribute '__dict__'
  • __Get__ revised method
class Str:
    def __init__(self, name):
        self.name = name

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str('name')

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


print(People.name)  # 完美,解决
get---> None <class '__main__.People'>
<__main__.Str object at 0x107a86da0>

Saber-rattling

class Str:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
        if not isinstance(value, self.expected_type):  # 如果不是期望的类型,则抛出异常
            raise TypeError('Expected %s' % str(self.expected_type))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Str('name', str)  # 新增类型限制str

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


try:
    p1 = People(123, 18, 3333.3)  # 传入的name因不是字符串类型而抛出异常
except Exception as e:
    print(e)
set---> <__main__.People object at 0x1084cd940> 123
Expected <class 'str'>

Bold and decisive

class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
        if not isinstance(value, self.expected_type):
            raise TypeError('Expected %s' % str(self.expected_type))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)


class People:
    name = Typed('name', str)
    age = Typed('name', int)
    salary = Typed('name', float)

    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


try:
    p1 = People(123, 18, 3333.3)
except Exception as e:
    print(e)
set---> <__main__.People object at 0x1082c7908> 123
Expected <class 'str'>
try:
    p1 = People('nick', '18', 3333.3)
except Exception as e:
    print(e)
set---> <__main__.People object at 0x1078dd438> nick
set---> <__main__.People object at 0x1078dd438> 18
Expected <class 'int'>
p1 = People('nick', 18, 3333.3)
set---> <__main__.People object at 0x1081b3da0> nick
set---> <__main__.People object at 0x1081b3da0> 18
set---> <__main__.People object at 0x1081b3da0> 3333.3
  • After sweeping we already can realize the function, but the problem is that if we have a lot of attributes of the class, you still use the class attribute in the definition of a bunch of ways to implement, low, this time I need to teach you a trick: Magic

103- descriptor (get, set, delete) - Magic .jpg

Class decorator: None Reference

def decorate(cls):
    print('类的装饰器开始运行啦------>')
    return cls


@decorate  # 无参:People = decorate(People)
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p1 = People('nick', 18, 3333.3)
类的装饰器开始运行啦------>

Class decorator: There are parameters

def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>', kwargs)
        return cls

    return decorate


@typeassert(
    name=str, age=int, salary=float
)  # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


p1 = People('nick', 18, 3333.3)
类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}

Swords

class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
        if not isinstance(value, self.expected_type):
            raise TypeError('Expected %s' % str(self.expected_type))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)


def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>', kwargs)
        for name, expected_type in kwargs.items():
            setattr(cls, name, Typed(name, expected_type))
        return cls

    return decorate


@typeassert(
    name=str, age=int, salary=float
)  # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary


print(People.__dict__)
p1 = People('nick', 18, 3333.3)
类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
{'__module__': '__main__', '__init__': <function People.__init__ at 0x10797a400>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None, 'name': <__main__.Typed object at 0x1080b2a58>, 'age': <__main__.Typed object at 0x1080b2ef0>, 'salary': <__main__.Typed object at 0x1080b2c18>}
set---> <__main__.People object at 0x1080b22e8> nick
set---> <__main__.People object at 0x1080b22e8> 18
set---> <__main__.People object at 0x1080b22e8> 3333.3

Descriptor summary

  • Descriptor can be achieved most underlying magic python class properties, including @ classmethod, @ staticmethd, @ property even attribute __slots__

  • Described in the parent is an important tool for many advanced libraries and frameworks, typically using a descriptor to a decorator or large frame assembly in metaclass.

103- descriptor (get, set, delete) - Personal Tailor .jpg

Custom @property

  • Use descriptor principle a complete custom @property, to achieve the delay calculation (essentially a function of the properties is made by using the principle of a decorator descriptor: the attribute dictionary function named class key, value describing the object class identifier generated)

Review property

class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @property
    def area(self):
        return self.width * self.length


r1 = Room('alex', 1, 1)
print(r1.area)
1

Custom property

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        return self.func(instance)  # 此时你应该明白,到底是谁在为你做自动传递self的事情


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty  # area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
    def area(self):
        return self.width * self.length


r1 = Room('alex', 1, 1)
print(r1.area)
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1

Achieve delay calculation function

class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            print('--->')
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)  # 计算一次就缓存到实例的属性字典中
            return value


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty  # area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
    def area(self):
        return self.width * self.length


r1 = Room('alex', 1, 1)
print(r1.area)  # 先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
--->
1
print(r1.area)  # 先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
1

Break delay calculation

  • A small change, the delay calculation on the broken dream
class Lazyproperty:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            value = self.func(instance)
            instance.__dict__[self.func.__name__] = value
            return value
        # return self.func(instance) # 此时你应该明白,到底是谁在为你做自动传递self的事情
    def __set__(self, instance, value):
        print('hahahahahah')


class Room:
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty  # area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
    def area(self):
        return self.width * self.length
print(Room.__dict__)
{'__module__': '__main__', '__init__': <function Room.__init__ at 0x107d53620>, 'area': <__main__.Lazyproperty object at 0x107ba3860>, '__dict__': <attribute '__dict__' of 'Room' objects>, '__weakref__': <attribute '__weakref__' of 'Room' objects>, '__doc__': None}
r1 = Room('alex', 1, 1)
print(r1.area)
print(r1.area)
print(r1.area)
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1
print(
    r1.area
)  #缓存功能失效,每次都去找描述符了,为何,因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了
这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
1

Custom @classmethod

class ClassMethod:
    def __init__(self, func):
        self.func = func

    def __get__(
            self, instance,
            owner):  #类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
        def feedback():
            print('在这里可以加功能啊...')
            return self.func(owner)

        return feedback


class People:
    name = 'nick'

    @ClassMethod  # say_hi=ClassMethod(say_hi)
    def say_hi(cls):
        print('你好啊,帅哥 %s' % cls.name)


People.say_hi()

p1 = People()
在这里可以加功能啊...
你好啊,帅哥 nick
p1.say_hi()
在这里可以加功能啊...
你好啊,帅哥 nick
  • Doubt, if there is a class method parameters do, say, say
class ClassMethod:
    def __init__(self, func):
        self.func = func

    def __get__(self, instance, owner
                ):  # 类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
        def feedback(*args, **kwargs):
            print('在这里可以加功能啊...')
            return self.func(owner, *args, **kwargs)

        return feedback


class People:
    name = 'nick'

    @ClassMethod  # say_hi=ClassMethod(say_hi)
    def say_hi(cls, msg):
        print('你好啊,帅哥 %s %s' % (cls.name, msg))


People.say_hi('你是那偷心的贼')

p1 = People()
在这里可以加功能啊...
你好啊,帅哥 nick 你是那偷心的贼
p1.say_hi('你是那偷心的贼')
在这里可以加功能啊...
你好啊,帅哥 nick 你是那偷心的贼

Custom @staticmethod

class StaticMethod:
    def __init__(self, func):
        self.func = func

    def __get__(
            self, instance,
            owner):  # 类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身
        def feedback(*args, **kwargs):
            print('在这里可以加功能啊...')
            return self.func(*args, **kwargs)

        return feedback


class People:
    @StaticMethod  # say_hi = StaticMethod(say_hi)
    def say_hi(x, y, z):
        print('------>', x, y, z)


People.say_hi(1, 2, 3)

p1 = People()
在这里可以加功能啊...
------> 1 2 3
p1.say_hi(4, 5, 6)
在这里可以加功能啊...
------> 4 5 6

103- descriptor (get, set, delete) - holiday .jpg

Guess you like

Origin www.cnblogs.com/nickchen121/p/10991295.html