描述器
所谓描述器,即实现了描述符协议,即对一个类变量实现get, set, 和 delete方法的对象。
简单例子:
1 | class Int: |
2 | def __init__(self, name): |
3 | self.name = name |
4 | |
5 | def __get__(self, instance, cls): |
6 | print('get {}'.format(self.name)) |
7 | if instance is not None: |
8 | return instance.__dict__[self.name] |
9 | return self |
10 | |
11 | def __set__(self, instance, value): |
12 | instance.__dict__[self.name] = value |
13 | |
14 | class A: |
15 | val = Int('val') |
16 | |
17 | def __init__(self): |
18 | self.val = 3 |
19 | |
20 | a = A() |
21 | a.val # 在获取类变量的时候是通过上述的定义的描述器Int获得的 |
22 | get val |
23 | 3 |
24 | a.__dict__ |
25 | {'val': 3} |
26 | |
27 | class Int: |
28 | def __init__(self, name): |
29 | self.name = name |
30 | self.data = {} |
31 | |
32 | def __get__(self, instance, cls): |
33 | print('get {}'.format(self.name)) |
34 | if instance is not None: |
35 | return self.data[instance] |
36 | return self |
37 | |
38 | def __set__(self, instance, value): |
39 | self.data[instance] = value |
40 | |
41 | class A: |
42 | val = Int('val') |
43 | |
44 | def __init__(self): |
45 | self.val = 3 |
46 | |
47 | a = A() |
48 | a.val ## 当一个类变量 实现了__get__和__set__方法之后,访问这个类变量会调用__get__方法,对这个变量赋值会调用__set__方法 这种类变量叫做描述器 |
49 | get val |
50 | 3 |
51 | a.__dict__ |
52 | {} |
描述器是一个类,实现了__get__
,__set__
,__delete__
中1个或多个方法
描述器事实上是一种代理机制
当一个类变量被定义为描述器,对这个类的操作,将由此描述器来代理
- 访问:
__get__
- 赋值:
__set__
- 删除:
__delete__
带
set,
delete方法的描述器会提升优先级到
dict之前
__get__(self, instance, cls)
instance 表示当前实例 cls表示类本身 使用类访问的时候 instance为None1
class Desc:
2
def __get__(self, instance, cls):
3
print(instance)
4
print(cls)
5
6
class A:
7
x = Desc()
8
9
A().x
10
<__main__.A object at 0x10561cb70> # A类的实例
11
<class '__main__.A'> # A类对象
12
13
A.x
14
None
15
<class '__main__.A'>
__set__(self, instance, value)
instance 表示当前实例 value为右值 只有实例才会调用set1
class Desc:
2
def __get__(self, instance, cls):
3
print(instance)
4
print(cls)
5
6
def __set__(self, instance, value):
7
print(instance)
8
print(value)
9
10
class A:
11
x = Desc()
12
13
A().x = 5
14
<__main__.A object at 0x10561cc50> # A类实例
15
5
16
17
A.x = 5
__delete__(self, instance)
instance 表示当前实例1
class Desc:
2
def __get__(self, instance, cls):
3
print(instance)
4
print(cls)
5
6
def __set__(self, instance, value):
7
print(instance)
8
print(value)
9
10
def __delete__(self, instance):
11
print(instance)
12
class A:
13
x = Desc()
14
15
del A().x
16
<__main__.A object at 0x1055fed30> # A类实例
描述器主要针对的是类变量,这个需要记得,可以针对类变量进行多重操作。
描述器的应用
- classmethod的实现
1 | from functools import partial |
2 | from functools import wraps |
3 | |
4 | class Classmethod: |
5 | def __init__(self, fn): |
6 | self.fn = fn |
7 | |
8 | def __get__(self, instance, cls): |
9 | return wraps(self.fn)(partial(self.fn, cls)) #classmethod最主要的即固定下第一个参数也类对象,所以需要固定下第一参数 |
10 | |
11 | class A: |
12 | NAME = 'jusene' |
13 |
|
14 | def cls_method(cls): # 这里结合装饰器的理解 |
15 | print(cls) |
16 | print(cls.NAME) |
17 | |
18 | A.cls_method() # A.class_method.__get__(None,A) |
19 | <class '__main__.A'> |
20 | jusene |
21 | A().cls_method() # A.class_method.__get__(A.instance,A) |
22 | <class '__main__.A'> |
23 | jusene |
- staticmethod的实现
1 | class Staticmethod: |
2 | def __init__(self, fn): |
3 | self.fn = fn |
4 | |
5 | def __get__(self, instance, cls): # staticmethod 忽略类对象和实例,所以这里只要返回自己的函数即可 |
6 | return self.fn |
7 | |
8 | class A: |
9 |
|
10 | def static_method(): |
11 | print('haha') |
12 | |
13 | A.static_method() |
14 | haha |
15 | A().static_method() |
16 | haha |
- property
1 | class Property: |
2 | def __init__(self, fget, fset=None, fdel=None): |
3 | self.fget = fget |
4 | self.fset = fset |
5 | self.fdel = fdel |
6 | def __get__(self, instance, cls): |
7 | if instance is not None: |
8 | return self.fget(instance) |
9 | return self |
10 | def __set__(self, instance, value): |
11 | if callable(self.fset): |
12 | self.fset(instance, value) |
13 | else: |
14 | raise AttributeError('{} can not assigneable'.format(self.fget.__name__)) |
15 | def __delete__(self, instance): |
16 | if callable(self.fdel): |
17 | self.fdel(instance) |
18 | else: |
19 | raise AttributeError('{} can not deleteable'.format(self.fget.__name__)) |
20 | def setter(self, fn): |
21 | self.fset = fn |
22 | return self |
23 | def deletter(self, fn): |
24 | self.fdel = fn |
25 | return self |
26 | |
27 | class A: |
28 | def __init__(self): |
29 | self.__x = 1 |
30 |
|
31 | def x(self): |
32 | return self.__x |
33 |
|
34 | def x(self, value): |
35 | self.__x = value |
36 |
|
37 | def x(self): |
38 | print('can not delete') |
39 | |
40 | a=A() |
41 | a.x |
42 | 1 |
43 | a.x = 3 |
44 | a.x |
45 | 3 |
46 | del a.x |
47 | can not delete |
描述器的使用场景
描述器的使用场景:用于接管对实例变量的操作
类型检测:
1 | import inspect |
2 | |
3 | class Person: |
4 | def __init__(self, name: str, age: int): |
5 | self.name = name |
6 | self.age = age |
7 | |
8 | p = Person(25,'jusene') # 无法针对类型注解约束 |
9 | |
10 | sig = inspect.signature(Person) |
11 | param = list(sig.parameters.items())[0][1] |
12 | param.annotation |
13 | str |
14 | |
15 | |
16 | class Typed: |
17 | def __init__(self, name, type): |
18 | self.name = name |
19 | self.type = type |
20 | |
21 | def __get__(self, instance, cls): |
22 | if instance is not None: |
23 | return instance.__dict__(self.name) |
24 | return self |
25 | |
26 | def __set__(self, instance, value): |
27 | if not isinstance(value, self.type): |
28 | raise TypeError() |
29 | instance.__dict__[self.name] = value |
30 | |
31 | class Person: |
32 | name = Typed('name', str) |
33 | age = Typed('age', int) |
34 | |
35 | def __init__(self, name: str, age: int): |
36 | self.name = name |
37 | self.age = age |
38 | |
39 | p = Person(25, 'instance') |
40 | TypeError |
41 | p = Person('instance', 25) |
42 | |
43 | |
44 | def typeassert(cls): |
45 | params = inspect.signature(cls).parameters |
46 | for name, param in params.items(): |
47 | if param.annotation != inspect._empty: |
48 | setattr(cls, name, Typed(name, param.annotation)) |
49 | return cls |
50 | |
51 |
|
52 | class Person: |
53 | def __init__(self, name: str, age: int, desc): |
54 | self.name = name |
55 | self.age = age |
56 | self.desc = desc |
1 | import datetime |
2 | from functools import wraps |
3 | from types import MethodType |
4 | |
5 | class Timeit: |
6 | def __init__(self, fn): |
7 | self.fn = fn |
8 | |
9 | def __get__(self, instance, cls): |
10 |
|
11 | def wrap(*args, **kwargs): |
12 | start = datetime.datetime.now() |
13 | ret = self.fn(*args, **kwargs) |
14 | cost = datetime.datetime.now() - start |
15 | instance.send(cost) |
16 | return ret |
17 | |
18 | if instance is not None: |
19 | return MethodType(wrap, instance) |
20 | return self |
21 | |
22 | class Sender: |
23 | def send(self, cost): |
24 | print(cost) |
25 | |
26 |
|
27 | def other(self): |
28 | pass |
29 | |
30 | s = Sender() |
31 | s.other() |