定义

Python的魔术方法是用于为我们自定义的类重写函数功能的方法

首先来看几个比较重要的

__getattribute__

这个方法会拦截所有企图对对象属性访问的动作,包括__dict__的访问,若不设置返回值会把None当作访问结果,但是不会拦截对类的属性访问,所以为了避免访问属性循环最好使用基类

但是只适用于新式类,就是集成自object或者type的类

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
26
27
class man(object):
gender="girl"
def __init__(self, name,age):
self.name= name
self.age =age
def __getattribute__(self,attr):
print("拦截")
try:
return super().__getattribute__(attr)
#避免循环
#展示下错误用法:self.__dict__[attr]
except AttributeError :
#注意这里的AttributeError异常是基类的__getattribute__抛出的
print(' have no attribute of %s'% attr)
A=man("kun",2.5)
print (man.gender)
print(A.gender)
print(A.name)
print(A.job)
# girl
# 拦截
# girl
# 拦截
# kun
# 拦截
# have no attribute of job
# None

当访问的属性不存在而且还重载了__getattribute__,那么他不会抛出AttributeError异常

__getattr__

执行条件有两个:

1.访问对象,

2.触发AttributeError异常

也就是说属性存在时也可以触发

访问对象不存在的属性时触发了AttributeError因此会自动调用

重载了__getattribute__方法,却没有主动抛出AttributeError异常的机制或者抛出其它类型的异常,__getattr__方法都不会执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class man(object):
gender="girl"
def __init__(self, name,age):
self.name= name
self.age =age
def __getattribute__(self,attr):
print("拦截")
if attr !='name':
raise AttributeError()
#这里主动抛出异常直接开始__getattr__
return super().__getattribute__(attr)
def __getattr__(self, attr):
print("执行__getattr__")
return self.name
A=man("kun",2.5)
print(A.gender)
# 拦截
# 执行__getattr__
# 拦截
# kun
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class man(object):
gender="girl"
def __init__(self, name,age):
self.name= name
self.age =age
def __getattribute__(self,attr):
print("拦截")
if attr !='name':
raise AttributeError()
#return super().__getattribute__(attr)
def __getattr__(self, attr):
print("执行__getattr__")
return self.name
A=man("kun",2.5)
print(A.gender)
# 拦截
# 执行__getattr__
# 拦截
# None

__setattr__

为属性赋值自动触发,__init__初始化也会触发

如果先搞在这个方法中为属性赋值,需要用__dict__返回一个字典修改键值对,这样就不会反复循环了

self.__dict__[key]=value

但是如果此时还定义了__getattribute__那么在访问self.__dict__属性也会触发__getattribute__

此时我们可以采用self.__dict__={xxxxx}这样的赋值语句就不会触发__getattribute__

———————分割线————————

__get__

描述符方法用于将访问对象属性转化为调用描述符方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Descriptor(object):
def __get__(self, obj, objtype):
print "get value=%s" % self.val
return self.val

def __set__(self, obj, val):
print "set value=%s" % val
self.val = val

class Stu(object):
age = Descriptor()

stu = Stu()
stu.age = 12 # set value=12
print stu.age # get value=12
# 12

__add__

1
2
3
num+9
等效于
num.__add__(9)

重写__add__实现字典组合

1
2
3
4
5
6
7
8
9
10
11
12
class Dic(dict):

def __add__(self, other):

self.update(other)
return Dic(self)

a = Dic({'de': 'Germany'})
b = Dic({'sk': 'Slovakia'})

print(a + b)
#{'de': 'Germany', 'sk': 'Slovakia'}

__init____str____del__

__init__类的构造方法,类似于__construct

在类初始化时调用

__str__重写了print函数

__del__类的析构方法,类似于__destruct

在类在内存中释放时调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person:

def __init__(self, name, rank):

self.name = name
self.rank = rank

def __str__(self):

return f'{self.name} is a {self.rank}'

def __del__(self):
print("我滴任务完成啦")

p = Person('穿山甲', '双料特工')
print(p)
#穿山甲 is a 双料特工
#我滴任务完成啦

__repr__

用于重写repr()函数,返回对象信息,可以用于代替__str__

repr()在shell中直接回显

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person:

def __init__(self, name, rank):

self.name = name
self.rank = rank

def __str__(self):

return f'{self.name} is a {self.rank}'

def __repr__(self):
return f'{self.name} 就是 {self.rank}'

p = Person('穿山甲', '双料特工')
print(p)
print(repr(p))
#穿山甲 is a 双料特工
#穿山甲 就是 双料特工

剩下几个都很好理解了…..

__module____call__

__module__表示当前操作的对象在哪个模块

__call__当对象被当作函数调用触发

__len____getitem__

__len__返回容器的长度

__getitem__定义访问([])运算符

__int____index__

__int__重写int()函数

__index__在调用hex(),oct(),bin()函数前把对象转化为int类型

__eq __,__ lt__,__gt__

__eq__()实现了==运算符

__lt__()实现了<运算符

__gt__()实现了>运算符

举个实现比较运算的例子:

1
2
3
4
5
6
7
8
def __gt__(self, other):

val1, val2 = self.__evaluate(other)

if val1 > val2:
return True
else:
return False