1.1 descriptor performance
Magic use three methods: GET (), the SET (), the Delete ()
method signature as follows:
. Object GET (Self, instance, owner)
. Object the SET (Self, instance, owner)
. Object the Delete (Self, instance )
Self refers to the current example, the caller
instance is the owner instance
owner belongs to the class attributes
class A: # 数据描述器的访问优先级要高于实例__dict__的访问;非数据描述器的访问,优先级要低于实例字典的访问
def __init__(self):
self.a1 = 'a1'
print('A init')
def __get__(self, instance, owner): # instance为owner的实例
print('get~~~~~~~~', self, instance, owner)
# print(self.__dict__)
return self
def __set__(self, instance, value): # 可以禁止修改实例的属性;主要看方法里的内容
print('set~~~~~~~~~~~', value, instance)
# self.data = value # 保存在A的实例里了
# setattr(instance, 'x', value) # 无限递归
# instance.data = value
# instance.__dict__['x'] = value
if instance:
raise Exception('不许改')
def __set_name__(self, owner, name): # python3.6新增的
print(owner, name)
self.name = name
class B: # 属主
x = A() # 类属性可以,描述器和属主类的类属性有关;解释器执行到这一句时,会调用__set_name__方法
z = 5
def __init__(self):
# self.y = A() # 实例属性不会,描述器与属主类的实例属性无关
self.x = 'b.x' # 动态增加属性
print('B init')
print('~~~~~~~~~~~~~~~~~')
print(B.x) # 会调用A实例的__get__方法;instance为None,B.x或B().x才会调用描述器
print(B.x.a1) # instance为None;注意是B.x调用了描述器
print('++++++++++++++++++')
b = B()
print(b.x) # instance为<__main__.B object at 0x0000000001E89668>
print(b.x.a1)
print('~~~~~~~~~~~~~~~')
b = B()
print(b.x) # <__main__.A object at 0x0000000000789630>
print(b.__dict__) # {'x': 'b.x'}
print('~~~~~~~~~~~~~~~~~~~~~~')
b.x = 500 # 会调用描述器的__set__方法
print(b.x) # <__main__.A object at 0x00000000027C9668>
print(b.__dict__) # {'x': 500}
print("++++++++++++++++")
B.x = 600 # 如果类的类属性x是描述器,那么不要使用这样的赋值语句
print(b.x) # 500
print(B.x) # 600
print('~~~~~~~~~~~~~~~')
b = B()
b.x = 100
print(b.x)
Method as defined __get__, is a class descriptor A, x B, or the class instance attributes of class B is read, a visiting instance of a class A, __get__ method is called.
Note that with the description of the class is the main class attributes, but not with the instance attribute. Access to the data descriptor of the instance dictionary takes precedence over access, access to non-data descriptor is lower than the priority of access to the instance dictionary.
1.2 Definition of descriptor
python in a class implements __get__, SET , __ any one delete__ three methods, that is descriptor. Implement some of these three methods, described to support the protocol.
- Achieved only __get__, non-data descriptor is non-data descriptor
- Achieved __get __, __ set__ data descriptor is the descriptor data
The descriptor 1.3python
python process (including staticmethod () and a classmethod ()) are implemented as a non data descriptor, and thus can redefine instance covering methods. property () function is implemented as a data descriptor. Thus, examples of behavior can not be overridden property.
class A:
@classmethod
def foo(cls): # 非数据描述符——> foo = classmethod(foo)
pass
@staticmethod
def bar(): # 非数据描述符
pass
@property
def z(self): # 数据描述符
return 5
def get_foo(self): # 非数据描述符
return self.foo
def __init__(self): # 非数据描述符
self.foo = 100
self.bar = 200
# self.z = 300 # 不能覆盖,会报错,因为z为数据描述符
a = A()
print(a.__dict__) # {'foo': 100, 'bar': 200}
print(A.__dict__)
Note: non-data class descriptors may be redefined and the cover in the examples, the class data descriptor and the cover can not be redefined in the examples.
Exercise
1. To achieve StaticMethod decorator
2. To achieve ClassMethod decorator
from functools import partial
class StaticMethod:
def __init__(self, fn):
self.fn = fn
def __get__(self, instance, owner):
return self.fn
class ClassMethod:
def __init__(self, fn):
self.fn = fn
def __get__(self, instance, owner):
return partial(self.fn, owner) # 固定owner即属主类
class D:
@StaticMethod # stmd = StaticMethod(stmd) 非数据描述器
def stmd(x, y):
print('static method', x, y)
@ClassMethod # foo = ClassMethod(foo)
def foo(cls, x, y):
print(cls.__name__, x, y)
d = D()
d.stmd(4, 5) # static method 4 5
d2 = D()
d2.foo(5, 6) # D 5 6
1.4 pairs efficacy data instance is
Ideas:
- Write function, first check in __init__, if unqualified, direct Throws
- Decorators, using inspect complete module
- Descriptor
first idea implementation code:
class Person:
def __init__(self, name: str, age: int):
params = ((name, str), (age, int))
if not self.check_data(params):
raise TypeError('类型错误')
self.name = name
self.age = age
def check_data(params):
for p, typ in params:
if not isinstance(p, typ):
return False
return True
# p1 = Person('tom', '20') # TypeError: 类型错误
The second idea of code to achieve:
from functools import wraps
import inspect
def type_check(cls):
@wraps(cls)
def wrapper(*args, **kwargs):
sig = inspect.signature(cls)
params = sig.parameters # OrderedDict
# print(params)
# values = list(params.values())
# keys = list(params.keys())
# for i, p in enumerate(args):
# if values[i].annotation != inspect._empty and not isinstance(p, values[i].annotation):
# raise TypeError('Wrong param={} {}'.format(keys[i], p))
for p, (k, v) in zip(args, params.items()):
if v.annotation is not v.empty and not isinstance(p, v.annotation):
raise TypeError('Wrong param= {} {}'.format(k, p))
for k, v in kwargs.items():
if params[k].annotation is not v.empty: # inspect._empty
if not isinstance(v, params[k].annotation):
raise TypeError('Wrong param={} {}'.format(k, v))
return cls(*args, **kwargs)
return wrapper
@type_check
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
p1 = Person('tony', 20)
p2 = Person('jacky', '18') # 直接报错
The third idea of code to achieve:
class TypeCheck: # 描述器
def __init__(self, typ):
self.type = typ
def __get__(self, instance, owner):
# print('get~~~~~~~~~~~~~~')
if instance:
return instance.__dict__[self.name]
else:
raise Exception # 或者return self,总之不正常
def __set__(self, instance, value):
# print('set~~~~~~~~~~~~~')
if instance:
if not isinstance(value, self.type):
raise TypeError(self.name, '+++++++++++')
else:
instance.__dict__[self.name] = value # 存回到属主类的实例字典中,默默的检查,不出错的话,像什么都没有发生一样
def __set_name__(self, owner, name): # python3.6新增的方法
print(name)
self.name = name
class Person:
name = TypeCheck(str) # 硬编码,不优雅
age = TypeCheck(int)
def __init__(self, name: str, age: int):
self.name = name # 会调用TypeCheck实例的__set__方法(描述器)
self.age = age
p3 = Person('curry', 31)
p4 = Person('durant', 29) # 直接抛出异常
print(p3.__dict__) # {'name': 'curry', 'age': 31}
print(p4.__dict__) # {'name': 'durant', 'age': 29}
The third process code implementation ideas, there is hard-coded, inelegant, the decorator can dynamically increase to the class method. Improved code is:
class TypeCheck: # 描述器
def __init__(self, name, typ):
self.name = name
self.type = typ
def __get__(self, instance, owner):
# print('get~~~~~~~~~~~~~~')
if instance:
return instance.__dict__[self.name]
else:
raise Exception # 或者return self,总之不正常
def __set__(self, instance, value):
# print('set~~~~~~~~~~~~~')
if instance:
if not isinstance(value, self.type):
raise TypeError(self.name, '+++++++++++')
else:
instance.__dict__[self.name] = value # 存回到属主类的实例字典中,默默的检查,不出错的话,像什么都没有发生一样
# def __set_name__(self, owner, name): # python3.6新增的方法
# print(name)
# self.name = name
def type_check(cls):
sig = inspect.signature(cls)
params = sig.parameters
# print(params) # 有序字典
for name, param in params.items():
if param.annotation is not param.empty:
setattr(cls, name, TypeCheck(name, param.annotation))
return cls
# 注意动态给类增加属性(方法)时,__set_name__方法并没有调用,即此方法无效
@type_check # Person = type_check(Person)
class Person:
# name = TypeCheck(str) # 硬编码,不优雅
# age = TypeCheck(int)
def __init__(self, name: str, age: int):
self.name = name # 会调用TypeCheck实例的__set__方法(描述器)
self.age = age
p3 = Person('curry', 31)
p4 = Person('durant', 29) # 直接抛出异常
print(p3.__dict__) # {'name': 'curry', 'age': 31}
print(p4.__dict__) # {'name': 'durant', 'age': 29}
print(Person.__dict__)
? Now you can use the function decorator class to dynamically add attributes, whether it would be changed to function decorator class decorator The answer is yes, see the detailed code:
class TypeCheck: # 描述器
def __init__(self, name, typ):
self.name = name
self.type = typ
def __get__(self, instance, owner):
# print('get~~~~~~~~~~~~~~')
if instance:
return instance.__dict__[self.name]
else:
raise Exception # 或者return self,总之不正常
def __set__(self, instance, value):
# print('set~~~~~~~~~~~~~')
if instance:
if not isinstance(value, self.type):
raise TypeError(self.name, '+++++++++++')
else:
instance.__dict__[self.name] = value # 存回到属主类的实例字典中,默默的检查,不出错的话,像什么都没有发生一样
# def __set_name__(self, owner, name): # python3.6新增的方法
# print(name)
# self.name = name
class TypeInject:
def __init__(self, cls):
self.cls = cls
def __call__(self, *args, **kwarg):
sig = inspect.signature(self.cls)
params = sig.parameters
# print(params) # OrderedDict([('name', <Parameter "name:str">), ('age', <Parameter "age:int">)])
for name, param in params.items():
# print(name, param.annotation)
if param.annotation != param.empty: # inspect._empty
setattr(self.cls, name, TypeCheck(name, param.annotation))
return self.cls(*args, **kwarg)
@TypeInject # Person = TypeInject(Person)
class Person:
def __init__(self, name: str, age: int):
self.name = name
self.age = age
p5 = Person('Green', 28)
All methods are described in the python is