table of Contents
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
Descriptor itself should be defined as the new class, the proxy class should be new class
Descriptor must be defined as a property of that class, and can not be defined to the constructor
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
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.
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