Python 的魔术方法

Python 的魔术方法

最近我开始读《流畅的 Python》这本书,它的豆瓣评分有 9.4 之高!也确实给了我不少的惊喜,学会了一些 Python 的高级用法。就像作者所说的那样,如果你不了解 Python 的精髓,你写出来的代码都是「带口音的」!

我们从一个很常见的需求说起。在调试代码过程中,会经常地打印一下对象,看一下它的内容是什么。

下面有一个带有 color 属性的 Cat 类。

1
2
3
4
5
6
7
class Cat(object):
def __init__(self, color):
self.color = color

new_cat = Cat("white")
print(new_cat)
# <__main__.Cat object at 0x7fa66cdfc9d0>

如果你直接打印它,就会得到 <__main__.Cat object at 0x7fa66cdfc9d0> 这样一个内存地址,这显然不是我们希望的结果。

魔法来了(巴啦啦能量——变!),可以通过自己在类中实现 __str__ 的内容,以重构 print 的输出。

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

def __str__(self):
return f"[str]: A {self.color} color cat."

new_cat = Cat("white")
print(new_cat)
# [str]: A white color cat.

我们可以清楚的从 [str]: A white color cat. 里获取到想要的信息,非常清晰易懂。这种在类中使用的双下划线方法就是 Python 的魔术方法,是不是很像一种魔法!当然它是由语言本身提供的。

除了 __str__ 之外,还有很多内置的魔术方法,比如 __add____getitem____len__ 等。接下来我要对比一下两个比较容易混淆的方法: __str____repr__

先放结果,简单来说:

  • __str__ 的目标是清晰易懂的表达对象,主要用于打印输出;
  • __repr__ 的目标是准确无误的表达对象,主要用于开发调试。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Cat(object):
def __init__(self, color):
self.color = color

def __str__(self):
return f"[str]: A {self.color} color cat."

def __repr__(self):
return f"[repr]: A {self.color} color cat."

new_cat = Cat("white")
print(new_cat)
# [str]: A white color cat.
print(str(new_cat))
# [str]: A white color cat.
print(repr(new_cat))
# [repr]: A white color cat.

# 在 ipython 中,打印变量和直接运行变量得到就是不同的结果
# In [1]: print(new_cat)
# [str]: A white color cat.
# In [2]: new_cat
# Out[2]: [repr]: A white color cat.

另外,当类中没有实现 __str__ 方法时,会默认使用 __repr__ 方法代替。而没有实现 __repr__ 方法时,默认输出内存地址,却不会用 __str__ 方法代替。

还记得开头的提到的那个问题吗?为什么要用 len(arr) 而不是 arr.len() ? 这个主要是由 Python 语言风格决定的,也就是常说的 Pythonic。学会了上述的魔法,你也可以写出流畅的 Pythonic 代码,快动手试试吧!

参考

评论