三个魔术方法:
__get__()
__set__()
__delete__()
object.__get__(self,实例名,owner) #owner = 属主 ,instance = 属主类owner的实例
object.__set__(self,实例名,value)
object.__delete__(self,实例名)
更改属性的行为,当属性等于实例的时候,则可以进行操作
例:
|
1
2
3
4
5
6
7
|
class
A:
def
__init__(
self
):
self
.a1
=
'a1'
class
B:
x
=
A()
def
__init__(
self
):
pass
|
这样是可以执行的,首先定义好了A
通过定义B的x属性,调用A()
相当于在B类中执行:
|
1
2
3
|
print
(A().a1)
x
=
A()
print
(x.a1)
|
这两个是等价的
标记执行顺序
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class
A:
def
__init__(
self
):
print
(
'init'
)
self
.a1
=
'a1'
class
B:
x
=
A()
def
__init__(
self
):
print
(
'B init'
)
print
(B.x.a1)
init
a1
class
A:
def
__init__(
self
):
print
(
'init'
)
self
.a1
=
'a1'
class
B:
x
=
A()
def
__init__(
self
):
print
(
'B init'
)
self
.x
=
100
b
=
B()
print
(B.x.a1)
init
B init
a1
|
涉及到字典的执行顺序,所以,print(b.x.a1)是不行的
|
1
2
|
print
(b.x.a1)
AttributeError:
'int'
object
has no attribute
'a1'
|
引入描述器
_
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
__get__(
self
,instance,owner)
class
A:
def
__init__(
self
):
print
(
'A init'
)
self
.a1
=
'a1'
def
__get__(
self
, instance, owner):
print
(
self
,instance,owner)
class
B:
x
=
A()
#A() 就是一个描述器,当对B()或B的实例的x的属性进行访问,则成为A()的实例的方式,则调用__get__方法
def
__int__(
self
):
print
(
'B init'
)
self
.x
=
100
print
(B.x.a1)
|
发现报错提示如下:
print(B.x.a1)
AttributeError: 'NoneType' object has no attribute 'a1'
提示 None类型是不能调用的,当通过一个属性访问,如果属性是另一个类的实例,而恰好这个类又实现了描述器的方法之一
当访问描述器的时候,如果是get触发则返回当前实例以及描述器属主的类型信息
所以,return返回为None的实例,则不能被调用
打印B.x 的类型,可看到为None
|
1
2
3
4
5
|
print
(B.x)
def
__get__(
self
, instance, owner):
print
(
self
,instance,owner)
<__main__.A
object
at
0x0000000000B88390
>
None
<
class
'__main__.B'
>
None
|
对B实例化后打印查看
|
1
2
3
4
|
print
(
'B.x : '
,B.x)
print
()
b
=
B()
print
(
'b.x.a1 : '
,b.x.a1)
|
返回如下:
|
1
2
3
4
5
6
7
|
A init
Traceback (most recent call last):
<__main__.A
object
at
0x0000000000DB80B8
>
None
<
class
'__main__.B'
>
B.x :
None
print
(
'b.x.a1 : '
,b.x.a1)
<__main__.A
object
at
0x0000000000DB80B8
> <__main__.B
object
at
0x0000000000DB83C8
> <
class
'__main__.B'
>
AttributeError:
'NoneType'
object
has no attribute
'a1'
|
发现依旧被拦截,所调用的是一个None类型
归根结底,都是与类属性有关系
|
1
2
|
b
=
B()
print
(B.x)
|
返回如下
|
1
2
3
|
A init
<__main__.A
object
at
0x0000000000718390
>
None
<
class
'__main__.B'
>
None
|
对照get定义的方法:
|
1
2
|
def
__get__(
self
, instance, owner):
print
(
self
,instance,owner)
|
执行效果如下:
|
1
2
3
|
A init
<__main__.A
object
at
0x0000000000718390
>
None
<
class
'__main__.B'
>
None
|
原来的实例返回是None,通过get方法变为了类的属性
|
1
2
3
4
|
b
=
B()
print
(B.x)
print
(
'-'
*
90
)
print
(b.x.a1)
|
凡是进入描述器的三个方法之一,都是会被拦截进行操作
返回如下所示:
|
1
2
3
4
5
6
|
A init
<__main__.A
object
at
0x0000000000858390
>
None
<
class
'__main__.B'
>
None
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
分别返回了
self
, instance, owner
<__main__.A
object
at
0x0000000000858390
> <__main__.B
object
at
0x0000000000836320
> <
class
'__main__.B'
>
|
当一个类的类属性等于另一个类的实例的时候,则实现了描述器方法,则是描述器的类
如果是类属性上访问的话,直接触发拦截
如果是实例属性访问,则不会访问描述器方法触发
解决返回值问题:return self
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
A:
def
__init__(
self
):
print
(
'A init'
)
def
__get__(
self
,instance,owner):
print
(
'A get'
,
self
,instance,owner)
return
self
class
B:
x
=
A()
def
__init__(
self
):
print
(
'B.init'
)
# print(B.x)
b
=
B()
print
(B.x)
|
返回如下:
|
1
2
3
4
|
A init
B.init
A get <__main__.A
object
at
0x0000000000DA6518
>
None
<
class
'__main__.B'
>
<__main__.A
object
at
0x0000000000DA6518
>
|
如果只是获取当前属性的手段,通过属性的描述器可以操作属主
这样可以解决不能访问的弊端
在遇到get中应该return一个有意义的值,至于return什么值合适,需要后期定义,具体就是可以获取属主的类及属性
如果仅实现了__get__,就是非数据描述符
同时实现了__set__ + __get__ 就是数据描述符
对日常来讲重要的是get和set同时出现
如果不是访问类的属性的话,则不会触发任何效果,只能是实例才会被拦截
__set__ 方法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class
A:
def
__init__(
self
):
print
(
'A init'
)
self
.a1
=
'a1'
def
__get__(
self
,instance,owner):
print
(
'!!!B get'
,
self
,instance,owner)
return
self
def
__set__(
self
,instance,value):
# #加入set之后,这里原本是为实例设置,但是触发了set
print
(
'~~A.set'
,
self
,instance,value)
class
B:
x
=
A()
def
__init__(
self
):
self
.x
=
100
b
=
B()
print
(b.x)
|
可以看到,首先被__set__方法先拦截
|
1
2
3
4
|
A init
~~A.
set
<__main__.A
object
at
0x0000000000DD45C0
> <__main__.B
object
at
0x0000000000DB7320
>
100
!!!B get <__main__.A
object
at
0x0000000000DD45C0
> <__main__.B
object
at
0x0000000000DB7320
> <
class
'__main__.B'
>
<__main__.A
object
at
0x0000000000DD45C0
>
|
对b.x进行跟进
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
A:
def
__init__(
self
):
print
(
'A init'
)
self
.a1
=
'a1'
def
__get__(
self
,instance,owner):
print
(
'!!!B get'
,
self
,instance,owner)
return
self
def
__set__(
self
,instance,value):
print
(
'~~A.set'
,
self
,instance,value)
class
B:
x
=
A()
def
__init__(
self
):
self
.x
=
10
|
对每个函数进行标记并跟进:
|
1
2
3
|
b
=
B()
A init
~~A.
set
<__main__.A
object
at
0x0000000000A945C0
> <__main__.B
object
at
0x0000000000A77320
>
100
|
当访问x属性,直接在A()中被__get__拦截
|
1
2
|
print
(b.x)
!!!B get <__main__.A
object
at
0x0000000000DA45C0
> <__main__.B
object
at
0x0000000000D87320
> <
class
'__main__.B'
|
查看类型字典
|
1
2
3
4
|
print
(b.__dict__)
{}
print
(B.__dict__)
{
'x'
: <__main__.A
object
at
0x0000000000D77588
>,
'__weakref__'
: <attribute
'__weakref__'
of
'B'
objects>,
'__doc__'
:
None
,
'__module__'
:
'__main__'
,
'__init__'
: <function B.__init__ at
0x0000000000DDAAE8
>,
'__dict__'
: <attribute
'__dict__'
of
'B'
objects>}
|
看到没有dict内容
照常来讲会修改dict,但是触发了set描述器,也就self.x = 这条语句没有被加入到dict
总结:
set如果对实例化中的属性定义,则对属性做修改
说到底就是如果实例的字典里没有,则去类的dict中去查找,set是对类的dict进行修改
通过这样的方式绕开了字典搜索
官方解释:有set,实例的优先级最高,如果没有set则类的优先级比较高
总结:
get:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class
A:
def
__init__(
self
,value
=
'abc'
):
self
.a1
=
value
def
__get__(
self
,instance,owner):
return
self
class
B:
x
=
A()
def
__init__(
self
):
self
.x
=
A(
123
)
print
(B.x)
print
(B.x.a1)
<__main__.A
object
at
0x0000000000DB84A8
>
abc
print
(b.x.a1)
123
print
(B.x.a1)
~~~~A__get__ <__main__.A
object
at
0x0000000000DA84A8
>
None
<
class
'__main__.B'
>
abc
|
print(b.__dict__),发现实例的dict中存在x方法
|
1
2
3
4
|
{
'x'
: <__main__.A
object
at
0x00000000006F7940
>}
print
(B.__dict__)
{
'__init__'
: <function B.__init__ at
0x0000000000E2AA60
>,
'__weakref__'
: <attribute
'__weakref__'
of
'B'
objects>,
'x'
: <__main__.A
object
at
0x00000000006F70F0
>,
'__dict__'
: <attribute
'__dict__'
of
'B'
objects>,
'__doc__'
:
None
,
'__module__'
:
'__main__'
}
set
:
|
只要设置相关的属性,实例方法添加不上dict,而set优先级别高,可以看到都是针对A的对象
print(b.x.a1) 被set先拦截
|
1
2
3
4
5
6
7
8
9
10
11
|
!!!!A__set__ <__main__.A
object
at
0x0000000000746550
> <__main__.B
object
at
0x0000000000727278
> <__main__.A
object
at
0x00000000007272B0
>
~~~~A__get__ <__main__.A
object
at
0x0000000000746550
> <__main__.B
object
at
0x0000000000727278
> <
class
'__main__.B'
>
abc
print
(B.x.a1)
!!!!A__set__ <__main__.A
object
at
0x0000000000726550
> <__main__.B
object
at
0x0000000000707278
> <__main__.A
object
at
0x00000000007072B0
>
~~~~A__get__ <__main__.A
object
at
0x0000000000726550
>
None
<
class
'__main__.B'
>
abc
print
(b.__dict__),发现实例的
dict
中不存在方法
{}
print
(B.__dict__)
{
'x'
: <__main__.A
object
at
0x0000000000DB7518
>,
'__module__'
:
'__main__'
,
'__init__'
: <function B.__init__ at
0x0000000000E1BAE8
>,
'__weakref__'
: <attribute
'__weakref__'
of
'B'
objects>,
'__doc__'
:
None
,
'__dict__'
: <attribute
'__dict__'
of
'B'
objects>}
|
一句话总结:一旦使用set,只能操作类属性
下面例子中,虽然会触发set,但是什么都没有操作
|
1
2
3
4
|
b
=
B()
b.xxx
=
777
!!!!A__set__ <__main__.A
object
at
0x0000000000BE6550
> <__main__.B
object
at
0x0000000000BC7278
>
777
{
'xxxx'
:
777
}
|
再访问的时候,再将实例返回回来,get就进行操作了
本质
主要看字典,一点点看到底修改了哪些,通过实例的方式无法修改属性
主要的特点是把实例从__dict__中去掉了,造成了该属性如果是数据描述则优先访问的假象
说到底,属性访问顺序就从来没有变过
一句话总结:非数据描述器可以覆盖,数据描述器直接修改类
在py中,所有的方法都是数据描述器
实现一个static装饰器
静态方法的本质
全局函数放到类中,使用时候,通过我们的类对象进行使用
|
1
2
3
4
5
6
7
8
9
|
class
A:
@
staticmethod
def
bar():
return
1
def
test(
self
):
return
2
f
=
A()
print
(f.test)
print
(f.bar)
|
查看结果
|
1
2
|
<bound method A.test of <__main__.A
object
at
0x0000000000D86278
>>
<function A.bar at
0x0000000000DF11E0
>
|
静态方法是作为一个function传递进来的
首先我们搞明白需求 如何调用的 A.foo 这么调用
基础框架
|
1
2
3
4
5
6
7
8
9
10
|
class
StaticMethod
:
def
__init__(
self
,fn):
self
.fn
=
fn
def
__get__(
self
,instance,owner):
print
(
self
,instance,owner)
class
A:
@
StaticMethod
def
foo():
print
(
'static'
)
print
(A.__dict__)
|
调用返回None,因为没有A的实例
|
1
2
3
|
a
=
A.foo
print
(a)
None
|
相当于在定义foo的时候被传递给StaticMethod(foo)
当前的foo相当于一个实例对象
返回的东西加了括号才可以调用,所以必须返回self
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class
Static_Method:
def
__init__(
self
,fn):
print
(
'fn:'
,fn)
self
.fn
=
fn
def
__get__(
self
,instance,owner):
print
(
self
,instance,owner)
return
self
.fn
class
A:
@Static_Method
def
foo():
print
(
'static'
)
f
=
A.foo
print
(
'f:'
,f)
|
这个foo原封不动的返回,打印他们的内存地址查看
|
1
2
3
|
fn: <function A.foo at
0x0000000000DEAA60
>
<__main__.Static_Method
object
at
0x0000000000A764E0
>
None
<
class
'__main__.A'
>
f: <function A.foo at
0x0000000000DEAA60
>
|
等价式:foo = Static_Method(foo)
就是说,调用的时候,必须以func类型传递到Statice_Method中
|
1
2
3
4
5
|
class
A:
# @Static_Met
def
foo():
print
(
'static'
)
print
(A.foo)
|
返回为:
<function A.foo at 0x0000000000E3F9D8>