python中的魔法函数

基本概念

在Python的类中,以两个下划线开头、两个下划线结尾的方法,如常见的 :initstrdel等,就被称为「魔术方法」(Magic methods)。魔术方法在类或对象的某些事件出发后会自动执行,如果希望根据自己的程序定制特殊功能的类,那么就需要对这些方法进行重写。使用这些「魔法方法」,我们可以非常方便地给类添加特殊的功能。

分类

  • 构造与初始化
  • 类的表示
  • 访问控制
  • 比较操作
  • 容器类操作
  • 可调用对象
  • 序列化

构造与初始化

1. new()

  • new(cls, […]) 是在一个对象实例化的时候所调用的第一个方法,所以它才是真正意义上的构造方法。
  • 它的第一个参数是这个类,其他的参数是用来直接传递给 init 方法。
  • new 决定是否要使用该 init 方法,因为 new 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例,如果 new 没有返回实例对象,则 init 不会被调用。
  • new 主要是用于继承一个不可变的类型比如一个 tuple 或者 string。
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 Person(object):

def __new__(cls, *args, **kwargs):
print("__new__()方法被调用了")
print('这个是*agrs', *args)
print('这个是kwagrs', **kwargs)

# cls表示这个类,剩余所有的参数传给__init__()方法,
# 若不返回,则__init__()不会被调用
return object.__new__(cls)

def __init__(self, name, age):
print("__init__()方法被调用了")
self.name = name
self.age = age
print(self.name, self.age)

p = Person("张三", 20)

# Output:
# __new__()方法被调用了
# 这个是*agrs 张三 20
# 这个是kwagrs
# __init__()方法被调用了
# 张三 20

2. init()

init()方法:构造器,当一个实例被创建的时候调用的初始化方法。

1
2
3
4
5
6
7
8
class Person(object):

def __init__(self, name, age):
self.name = name
self.age = age

p1 = Person('张三', 20)
p2 = Person('李四', 22)

3. del()

del()方法:析构器,当一个实例被销毁时自动调用的方法。

1
2
3
4
5
6
7
8
9
10
class Washer:
def __del__(self):
"""
当删除对象时,解释器会自动调用del方法
"""
print('对象已删除!')

haier = Washer()
# output:
# 对象已删除!

类的表示

1. str() 和repr()

repr的结果是让解释器用的,str的结果是让人看的

1
2
3
4
5
6
7
8
9
10
11
12
13
class Washer:
def __int__(self):
pass

def __repr__(self):
return '我是__repr__()魔法方法!'

def __str__(self):
"""
这个str的作用就是:类的说明或对象状态的说明
:return:
"""
return '我是__str__魔法方法!'

3. bool()

当调用 bool(obj) 时,会调用 bool() 方法,返回 True 或 False

1
2
3
4
5
6
7
8
9
10
11
class Person(object):
def __init__(self, uid):
self.uid = uid

def __bool__(self):
return self.uid > 10

p1 = Person(4)
p2 = Person(14)
print(bool(p1)) # False
print(bool(p2)) # True

访问控制

  • setattr:定义当一个属性被设置时的行为
  • getattr:定义当用户试图获取一个不存在的属性时的行为
  • delattr:删除某个属性时调用
  • getattribute:访问任意属性或方法时调用
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
28
29
30
31
32
class Person(object):

def __setattr__(self, key, value):
"""属性赋值"""
if key not in ('name', 'age'):
return
if key == 'age' and value < 0:
raise ValueError()
super(Person, self).__setattr__(key, value)

def __getattr__(self, key):
"""访问某个不存在的属性"""
return 'unknown'

def __delattr__(self, key):
"""删除某个属性"""
if key == 'name':
raise AttributeError()
super().__delattr__(key)

def __getattribute__(self, key):
"""所有属性/方法调用都经过这里"""
if key == 'money':
return 100
elif key == 'hello':
return self.say
return super().__getattribute__(key)

p1 = Person()
p1.name = '张三' # 调用__setattr__
p1.age = 20 # 调用__setattr__
print(p1.name, p1.age) # 张三 20

容器类操作

容器类的魔法方法,主要包括:

  • setitem(self, key, value):定义设置容器中指定元素的行为,相当于 self[key] = value;

  • getitem(self, key): 定义获取容器中指定元素的行为,相当于 self[key];

  • delitem(self, key):定义删除容器中指定元素的行为,相当于 del self[key];

  • len(self):定义当被 len() 调用时的行为(返回容器中元素的个数);

  • iter(self):定义当迭代容器中的元素的行为;

  • contains(self, item):定义当使用成员测试运算符(in 或 not in)时的行为;

  • reversed(self):定义当被 reversed() 调用时的行为。

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
28
29
30
31
32
33
34
35
36
37
38
class MyList(object):

def __init__(self, values=None):
self.values = values
self._index = 0

def __setitem__(self, key, value):
self.values[key] = value

def __getitem__(self, key):
return self.values[key]

def __delitem__(self, key):
del self.values[key]

def __len__(self):
return len(self.values)

def __iter__(self):
return self

def __next__(self):
if self._index >= len(self.values):
raise StopIteration
value = self.values[self._index]
self._index += 1
return value

def __contains__(self, key):
return key in self.values

def __reversed__(self):
return list(reversed(self.values))


if __name__ == '__main__':
my_list = MyList([1, 2, 3, 4, 5])
print(len(my_list))

可调用对象

在Python中,方法也是一种高等的对象。这意味着他们也可以像其他对象一样被传递到方法中,这是一个非常惊人的特性。 Python中有一个特殊的魔术方法可以让类的实例的行为表现的像函数一样,你可以调用他们,将一个函数当做一个参数传到另外一个函数中等等。这个魔法方法就是 call(self, [args…])。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Circle(object):

def __init__(self, x, y):
self.x = x
self.y = y

def __call__(self, x, y):
self.x = x
self.y = y

a = Circle(10, 20) # __init__
print(a.x, a.y) # 10 20

a(100, 200) # 此时a这个对象可以当做一个方法来执行,这是__call__魔法方法的功劳
print(a.x, a.y) # 100 200

序列化

  • getstate()

  • setstate()

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
28
import pickle

class Person(object):

def __init__(self, name, age, birthday):
self.name = name
self.age = age
self.birthday = birthday

def __getstate__(self):
# 执行 pick.dumps 时 忽略 age 属性
return {
'name': self.name,
'birthday': self.birthday
}

def __setstate__(self, state):
# 执行 pick.loads 时 忽略 age 属性
self.name = state['name']
self.birthday = state['birthday']

person = Person('李四', 20, (2017, 2, 23))
pickled_person = pickle.dumps(person) # 自动执行 __getstate__ 方法

p = pickle.loads(pickled_person) # 自动执行 __setstate__ 方法
print(p.name, p.birthday) # 李四 (2017, 2, 23)
# 由于执行 pick.loads 时 忽略 age 属性,所以下面执行回报错
print(p.age) # AttributeError: 'Person' object has no attribute 'age'

参考文献

  1. https://www.cnblogs.com/Jimmy1988/p/6801795.html
  2. https://www.jb51.net/article/85719.htm
  3. https://blog.csdn.net/weixin_45901519/article/details/110482428