蒙珣的博客

未来很长,当有勇气面对,当与自己和解。

0%

Python魔法方法

在Python中,所有以双下划线__包起来的方法,统称为Magic Method(魔术方法),它是一种的特殊方法,普通方法需要调用,而魔术方法不需要调用就可以自动执行。

魔术方法在类或对象的某些事件出发后会自动执行,让类具有神奇的“魔力”。如果希望根据自己的程序定制自己特殊功能的类,那么就需要对这些方法进行重写。

Python中常用的运算符、for循环、以及类操作等都是运行在魔术方法之上的。

__doc__

  • 表示类的描述信息
1
2
3
4
5
6
class Foo:
"""描述类信息,这个是工具类"""
def func(self):
pass

print(Foo.__doc__) # 输出:类的描述信息

__module__ 和 __class__

  • __module__表示当前操作的对象在那个模块
  • __class__表示当前操作的对象的类是什么

test.py

1
2
3
class Person:
def __init__(self):
self.name = 'william'

main.py

1
2
3
4
5
from test import Person

obj = Person
print(obj.__module__) # 输出 test 即:输出模块
print(obj.__class__) # 输出 test.Person 即:输出类

结果

1
2
test
<class 'test.Person'>

__init__

  • 初始化方法,通过类创建对象时,自动触发执行
1
2
3
4
5
6
class Person:
def __init__(self, name):
self.name = name
self.age = 18

obj = Person('william') # 自动执行类中的 __init__ 方法

__del__

  • 当对象在内存中被释放时,自动触发执行

此方法一般无需定义,__del__的调用是由解释器在进行垃圾回收时自动触发执行的

1
2
3
class Foo:
def __del__(self):
pass

__call__

  • 对象后面加括号,触发执行

__init__方法的执行是由创建对象触发的,即:对象 = 类名();而对于 __call__方法的执行是由对象后加括号触发的,即对象()或者类()()

1
2
3
4
5
6
7
8
9
class Foo:
def __init__(self):
pass

def __call__(self, *args, **kwargs):
print("__call__")

obj = Foo() # 执行 __init__
obj() # 执行 __call__

__dict__

  • 类或对象中的所有属性

类的实例属性属于对象;类中的类属性和方法属于类,即:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Province:
country = "China"

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

def func(self, *args, **kwargs):
print("func")

# 获取类的属性,即:类属性、方法
print(Province.__dict__)
# 输出: {'__module__': '__main__', 'country': 'China', '__init__': <function Province.__init__ at 0x100bd6160>, 'func': <function Province.func at 0x100c24f40>, '__dict__': <attribute '__dict__' of 'Province' objects>, '__weakref__': <attribute '__weakref__' of 'Province' objects>, '__doc__': None}


obj1 = Province("山东", 10000)
print(obj1.__dict__)
# 获取 对象obj1 的属性
# 输出: {'name': '山东', 'count': 10000}

obj2 = Province("山西", 20000)
print(obj2.__dict__)
# 获取 对象obj2 的属性
# 输出: {'name': '山西', 'count': 20000}

__str__

  • 如果一个类定义了__str__方法,那么打印对象时,默认输出该方法返回值
1
2
3
4
5
6
class Foo:
def __str__(self):
return 'william'

obj = Foo()
print("当前对象返回值为: %s" %obj) # 输出: william

__repr__

函数__str__ 用于将值转化为适于人阅读的形式,而__repr__转化为供解释器读取的形式,某对象没有适于人阅读的解释形式的话,__str__会返回与__repr__,所以print展示的都是__str__的格式。

默认情况下,__repr__ 会返回和调用者有关的 类名+object at+内存地址 (<__main__.CLanguage object at 0x000001A7275221D0>)信息。当然,我们还可以通过在类中重写这个方法,从而实现当输出实例化对象时,输出我们想要的信息。

1
2
3
4
5
6
7
8
class CLanguage:
def __init__(self):
self.name = "C语言中文网"
self.add = "http://c.biancheng.net"
def __repr__(self):
return "CLanguage[name="+ self.name +",add=" + self.add +"]"
clangs = CLanguage()
print(clangs)

结果

1
CLanguage[name=C语言中文网,add=http://c.biancheng.net]

__getitem__、__setitem__、__delitem__

  • 用于索引操作,如字典。已上分别表示获取、设置、删除数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Foo:

def __getitem__(self, key):
print("__getitem__", key)

def __setitem__(self, key, value):
print("__setitem__", key, value)

def __delitem__(self, key):
print("__delitem__", key)

obj = Foo()

result = obj['k1'] # 自动触发执行 __getitem__
obj['k2'] = 'william' # 自动触发执行 __setitem__
del obj['k1'] # 自动触发执行 __delitem__

我们可以自定义“有序字典”类,它允许我们按照元素插入的顺序进行迭代,并且同时支持通过键来访问、修改和删除元素

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class OrderedDict:  
def __init__(self):
self.keys = []
self.values = {}

def __getitem__(self, key):
# 实现通过键访问值的功能
if key not in self.values:
raise KeyError(f"Key {key} not found")
return self.values[key]

def __setitem__(self, key, value):
# 实现通过键设置值的功能,并维护键的顺序
if key not in self.keys:
self.keys.append(key)
self.values[key] = value

def __delitem__(self, key):
# 实现通过键删除元素的功能,并维护键的顺序
if key not in self.values:
raise KeyError(f"Key {key} not found")
self.keys.remove(key)
del self.values[key]

def __iter__(self):
# 实现迭代功能,按照键的插入顺序迭代值
for key in self.keys:
yield self.values[key]

def __repr__(self):
# 提供字符串表示形式,方便查看内容
return f"OrderedDict({repr(dict(zip(self.keys, self.values)))})"

# 使用示例
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3

# 访问元素
print(od['b']) # 输出: 2

# 修改元素
od['b'] = 20
print(od['b']) # 输出: 20

# 删除元素
del od['c']

# 迭代元素
for value in od:
print(value) # 输出: 1 和 20(按插入顺序)

# 查看内容
print(od) # 输出: OrderedDict({'a': 1, 'b': 20})

在这个例子中,OrderedDict 类使用两个内部数据结构:一个列表 keys 用于保存键的插入顺序,一个字典 values 用于保存键值对。通过实现 __getitem____setitem____delitem__ 方法,我们可以像操作普通字典一样操作 OrderedDict 实例,同时还保留了元素的插入顺序。

__getslice__、__setslice__、__delslice__

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

def __getslice__(self, i, j):
print('__getslice__', i, j)

def __setslice__(self, i, j, sequence):
print('__setslice__', i, j)

def __delslice__(self, i, j):
print('__delslice__', i ,j)

obj = Foo()

obj[-1:1] # 自动触发执行 __getslice__
obj[0:1] = [11,22,33,44] # 自动触发执行 __setslice__
del obj[0:2] # 自动触发执行 __delslice__

__iter__和__next__

__iter__它允许一个对象定义自己的迭代行为。当你尝试在一个对象上使用迭代(例如,在 for 循环中或在内置函数如 list()sum() 中)时,Python会查找这个对象是否实现了 __iter__ 方法。

__iter__ 方法应该返回一个迭代器对象,该对象实现了 __iter____next__ 方法。迭代器是一个可以记住遍历的位置的对象。

以下是一个简单的例子,演示如何在一个自定义类中实现 __iter__ 方法:

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 Fib:

def __init__(self, max):
self.max = max
self.a = 0
self.b = 1

def __iter__(self):
print("__iter__ called")
# 这里返回self, 因为self对象实现了__next__方法
return self

def __next__(self):
print("__next__ called")

fib = self.a
if fib > self.max:
# 当没有更多值可以返回时,__next__方法会抛出一个StopIteration异常,告诉for循环迭代结束
raise StopIteration
self.a, self.b = self.b, self.a + self.b
return fib

if __name__ == '__main__':
for i in Fib(5):
print(i)

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__iter__ called
__next__ called
0
__next__ called
1
__next__ called
1
__next__ called
2
__next__ called
3
__next__ called
5
__next__ called

定义 iter 表示这个类是一个迭代器(iterator)。它只在迭代开始的时候运行一次。返回的是对象本身。这里还给顺手给对象添加了 a 和 b 两个属性。接下来就是循环调用 next 直到遇到 raise StopIteration 为止。调用的过程就是模拟斐波那契数列的过程。