Python 描述器 Descriptors 详解
1、概念介绍
-
描述器 是两个类之间的事 -
描述器一般会用到4个魔术方法,如下: class A:
def __get__(self, instance, owner):
pass
def __set__(self, instance, value):
pass
def __delete__(self, instance):
pass
def __set_name__(self, owner, name):
pass
-
Python 中,一个类实现了__get__ 、 __set__ 、 __delete__ 三个方法中的任何一个方法,就是描述器。实现这三个中的某些方法,就支持了描述器协议 -
仅实现了__get__ ,就是 非数据描述器 non-data descriptor -
实现了__get__ 和__set__ ,就是 数据描述器 data descriptor -
如果一个类的类属性设置为描述器实例,那么它就称为owner 属主 -
当该类的类属性被查找、设置和删除时,就会调用描述器相应的方法 -
属性查找顺序:数据描述器 优先于 实例的__dict__ 优先于 非数据描述器 -
__delete__ 方法有同样的效果,有了这个方法,也是数据描述器 -
数据描述器可以阻止实例对属性的修改
2、通过示例验证
2.1 正常的类属性访问
class A:
def __init__(self):
print('A init')
self.a = 'AAA'
class B:
x = A()
def __init__(self):
print('B init')
self.a = 'BBB'
print(1, B.x)
print(2, B().x)
print(3, B.x.a)
print(4, B().x.a)
b1 = B()
print(5, b1.x)
print(6, b1.x.a)
print(7, b1.a)
print(8, b1.__dict__)
A init
1 <__main__.A object at 0x000001A4A049C580>
B init
2 <__main__.A object at 0x000001A4A049C580>
3 AAA
B init
4 AAA
B init
5 <__main__.A object at 0x000001A4A049C580>
6 AAA
7 BBB
8 {'a': 'BBB'}
2.2 非数据描述器且实例没有同名属性
class A:
def __init__(self):
print('A init')
self.a = 'AAA'
def __get__(self, instance, owner):
print("[=> A.__get__.\nself is {}.\ninstance is {}.\nowner is {}.<=]".format(self, instance, owner))
return self
class B:
x = A()
def __init__(self):
print('B init')
self.a = 'BBB'
self.b = A()
print('+' * 55)
print(1, B.x)
print(2, B.x.a)
print('*' * 55)
print(3, B().x)
print(4, B().x.a)
print('=' * 55)
b1 = B()
print(5, b1.x)
print(6, b1.x.a)
print('+' * 55)
print(7, b1.b)
print(8, b1.__dict__)
A init
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is None.
owner is <class '__main__.B'>.<=]
1 <__main__.A object at 0x000001A4A23F1550>
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is None.
owner is <class '__main__.B'>.<=]
2 AAA
*******************************************************
B init
A init
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is <__main__.B object at 0x000001A4A202EFA0>.
owner is <class '__main__.B'>.<=]
3 <__main__.A object at 0x000001A4A23F1550>
B init
A init
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is <__main__.B object at 0x000001A4A202E070>.
owner is <class '__main__.B'>.<=]
4 AAA
=======================================================
B init
A init
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is <__main__.B object at 0x000001A4A203C790>.
owner is <class '__main__.B'>.<=]
5 <__main__.A object at 0x000001A4A23F1550>
[=> A.__get__.
self is <__main__.A object at 0x000001A4A23F1550>.
instance is <__main__.B object at 0x000001A4A203C790>.
owner is <class '__main__.B'>.<=]
6 AAA
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 <__main__.A object at 0x000001A4A203CC40>
8 {'a': 'BBB', 'b': <__main__.A object at 0x000001A4A203CC40>}
2.3 非数据描述器且实例有同名属性
class A:
def __init__(self):
print('A init')
self.a = 'AAA'
def __get__(self, instance, owner):
print("[=> A.__get__.\nself is {}.\ninstance is {}.\nowner is {}.<=]".format(self, instance, owner))
return self
class B:
x = A()
def __init__(self):
print('B init')
self.a = 'BBB'
self.b = A()
self.x = 'x in class __init__'
print('+' * 55)
print('*' * 55)
print(3, B().x)
print('=' * 55)
b1 = B()
print(5, b1.x)
print('+' * 55)
print(7, b1.b)
print(8, b1.__dict__)
A init
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
*******************************************************
B init
A init
3 x in class __init__
=======================================================
B init
A init
5 x in class __init__
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 <__main__.A object at 0x000002041BEFFF10>
8 {'a': 'BBB', 'b': <__main__.A object at 0x000002041BEFFF10>, 'x': 'x in class __init__'}
2.4 数据描述器且实例有同名属性
class A:
def __init__(self):
print('A init')
self.a = 'AAA'
def __get__(self, instance, owner):
print("[=> A.__get__.\nself is {}.\ninstance is {}.\nowner is {}.<=]".format(self, instance, owner))
return self
def __set__(self, instance, value):
print("[=> A.__set__.\nself is {}.\ninstance is {}.\nvalue is {}.<=]".format(self, instance, value))
class B:
x = A()
def __init__(self):
print('B init')
self.a = 'BBB'
self.b = A()
print('================ before B init x ================')
self.x = 'x in class __init__'
print('================ after B init x ================')
print('=' * 55)
b1 = B()
print(5, b1.x)
print(6, b1.x.a)
print('+' * 55)
print(7, b1.b)
print(8, b1.__dict__)
A init
=======================================================
B init
A init
================ before B init x ================
[=> A.__set__.
self is <__main__.A object at 0x000002041BF0DA00>.
instance is <__main__.B object at 0x000002041BEFFF70>.
value is x in class __init__.<=]
================ after B init x ================
[=> A.__get__.
self is <__main__.A object at 0x000002041BF0DA00>.
instance is <__main__.B object at 0x000002041BEFFF70>.
owner is <class '__main__.B'>.<=]
5 <__main__.A object at 0x000002041BF0DA00>
[=> A.__get__.
self is <__main__.A object at 0x000002041BF0DA00>.
instance is <__main__.B object at 0x000002041BEFFF70>.
owner is <class '__main__.B'>.<=]
6 AAA
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 <__main__.A object at 0x000002041BF05E80>
8 {'a': 'BBB', 'b': <__main__.A object at 0x000002041BF05E80>}
2.5 数据描述器且实例有同名属性,验证属性查找顺序,无限递归
class A:
def __init__(self):
print('A init')
self.a = 'AAA'
def __get__(self, instance, owner):
print("[=> A.__get__.\nself is {}.\ninstance is {}.\nowner is {}.<=]".format(self, instance, owner))
return self
def __set__(self, instance, value):
print("[=> A.__set__.\nself is {}.\ninstance is {}.\nvalue is {}.<=]".format(self, instance, value))
setattr(instance, 'x', value)
class B:
x = A()
def __init__(self):
print('B init')
self.a = 'BBB'
self.b = A()
print('=== before B init x ===')
self.x = 'x in class __init__'
print('=== after B init x ===')
print('=' * 55)
b1 = B()
print(5, b1.x)
print(6, b1.x.a)
print('+' * 55)
print(7, b1.b)
print(8, b1.__dict__)
2.6 数据描述器且实例有同名属性,验证属性查找顺序,解决无限递归
class A:
def __init__(self):
print('A init')
self.a = 'AAA'
def __get__(self, instance, owner):
print("[=> A.__get__.\nself is {}.\ninstance is {}.\nowner is {}.<=]".format(self, instance, owner))
return self
def __set__(self, instance, value):
print("[=> A.__set__.\nself is {}.\ninstance is {}.\nvalue is {}.<=]".format(self, instance, value))
instance.__dict__['x'] = value
class B:
x = A()
def __init__(self):
print('B init')
self.a = 'BBB'
self.b = A()
print('=== before B init x ===')
self.x = 'x in class __init__'
print('=== after B init x ===')
b1 = B()
print('=' * 55)
print(5, b1.x)
print(6, b1.x.a)
print('+' * 55)
print(7, b1.b)
print(8, b1.__dict__)
A init
B init
A init
=== before B init x ===
[=> A.__set__.
self is <__main__.A object at 0x00000125759579A0>.
instance is <__main__.B object at 0x0000012575957A00>.
value is x in class __init__.<=]
=== after B init x ===
=======================================================
[=> A.__get__.
self is <__main__.A object at 0x00000125759579A0>.
instance is <__main__.B object at 0x0000012575957A00>.
owner is <class '__main__.B'>.<=]
5 <__main__.A object at 0x00000125759579A0>
[=> A.__get__.
self is <__main__.A object at 0x00000125759579A0>.
instance is <__main__.B object at 0x0000012575957A00>.
owner is <class '__main__.B'>.<=]
6 AAA
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 <__main__.A object at 0x0000012575957D00>
8 {'a': 'BBB', 'b': <__main__.A object at 0x0000012575957D00>, 'x': 'x in class __init__'}
2.7 数据描述器且实例有同名属性,禁止实例同名属性覆盖
class A:
def __init__(self):
print('A init')
self.a = 'AAA'
def __get__(self, instance, owner):
print("[=> A.__get__.\nself is {}.\ninstance is {}.\nowner is {}.<=]".format(self, instance, owner))
return self
def __set__(self, instance, value):
print("[=> A.__set__.\nself is {}.\ninstance is {}.\nvalue is {}.<=]".format(self, instance, value))
if instance:
raise AttributeError('can\'t set attribute')
class B:
x = A()
def __init__(self):
print('B init')
self.a = 'BBB'
self.b = A()
print('=== before B init x ===')
self.x = 'x in class __init__'
print('=== after B init x ===')
print('+' * 55)
b = B()
print('=' * 55)
print(b.x)
A init
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
B init
A init
=== before B init x ===
[=> A.__set__.
self is <__main__.A object at 0x00000204606C4160>.
instance is <__main__.B object at 0x0000020460C51A60>.
value is x in class __init__.<=]
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
AttributeError: can't set attribute
class A:
def __init__(self):
self._x = 5
@property
def x(self):
return self._x
a = A()
print(a)
print(a.x)
a.x = 100
2.8 __set_name__
class A:
def __init__(self):
print(1, 'A init')
def __get__(self, instance, owner):
print(2, '__get__', self, instance, owner)
return self
def __set_name__(self, owner, name):
print(3, '__set_name__', self, owner, name)
self.name = name
class B:
x = A()
print('=' * 30)
print(B().x)
print('*' * 30)
print(B().x.__dict__)
1 A init
3 __set_name__ <__main__.A object at 0x0000020460E7EEE0> <class '__main__.B'> x
==============================
2 __get__ <__main__.A object at 0x0000020460E7EEE0> <__main__.B object at 0x00000204606F4160> <class '__main__.B'>
<__main__.A object at 0x0000020460E7EEE0>
******************************
2 __get__ <__main__.A object at 0x0000020460E7EEE0> <__main__.B object at 0x0000020460751B50> <class '__main__.B'>
{'name': 'x'}
3、描述器应用
class A:
def __init__(self):
self.clsmtd = 100
self.stamtd = 200
self.foo = 300
@classmethod
def clsmtd(cls):
pass
@staticmethod
def stamtd():
pass
@property
def ppty(self):
return 5
def foo(self):
return self.foo
a = A()
print(a.__dict__)
print(A.__dict__)
{'clsmtd': 100, 'stamtd': 200, 'foo': 300}
{'__module__': '__main__', '__init__': <function A.__init__ at 0x0000020460456C10>, 'clsmtd': <classmethod object at 0x0000020460D07FD0>, 'stamtd': <staticmethod object at 0x0000020460D07730>, 'ppty': <property object at 0x000002045F136C70>, 'foo': <function A.foo at 0x000002045F0D2DC0>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
|