目录:
前段时间(年前)撸完了 Fluent Python,感觉非常不错,大概是看过的 Python 教材中最好的一本了。想整理一下,由于工作比较忙(懒)一直没时间,现在稍微总结一下(挖个坑)。
首先看两个很酷的例子:
1. 用 Python 表示一副扑克牌
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
2. 用 Python 表示二维向量
from math import hypot
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Vector(%r,%r)' % (self.x, self.y)
def __abs__(self):
return hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
这两个例子个人觉得都算是比较优雅的。其中最关键的一点是,Python 中的特殊函数形式都是 __xxx__ ,想要一个对象能够“做一件很多类都能做的事”,只需要实现对应的特殊函数就行了。(后面还会说明,Python 中的一些特性,如切片,多维数组,也都是特殊函数的语法糖)实现了 __len__ 就可以对其调用 len() 函数
>>> deck = FrenchDeck()
>>> len(deck)
52实现了 __getitem__ 就可以对其索引
>>> deck[0]
Card(rank='2', suit='spades')
>>> deck[-1]
Card(rank='A', suit='hearts')想要把一个类表示为字符串,有两个方法,__str__ 和 __repr__。
前者的目标是可读性,(看一眼里面我所关心的变量值是什么),后者的目标是明确性(用这个字符串可以创建完整的类 eval(repr(c)) == c)。
print方法会首先查找一个对象的 __str__ 方法,如果没找到则 fallback 到__repr__。
也可以指定 %s 格式化,使用 __str__ 方法,指定 %r 格式化,使用 __repr__ 方法。(一般来说,__str__ 就够用了)实现 __add__ 用于加法,__mul__ 用于乘法,__rmul__ 用于右乘
在一个需要判断对象x为 True 或者 False 的环境中(if, while...),Python会调用 bool(x)。
bool(x)首先调用 x.__bool__(),如果没有该方法则调用 x.__len__(),返回0就是 False,否则就是 True。
对 Python 的基本类型进行布尔判定时,是有规律可循的,基本上你觉得应该是 False 的,那么就是 False。比如数值0,空字符串,空列表,空集合等等,除此之外都是 True。(前两天有同学表示不知道 0 是 True 还是 False,我还是挺惊讶的)
常用特殊函数一览表:
非操作符重载类:
操作符重载类:
总结:
本文主要介绍了 Python 的“体制内”函数(雾)。实现一个 Python 类的时候,可以首先考虑遵循体制内潜规则,这样便可以获得体制内福利。换句话说,实现了某个特殊函数不仅仅代表着支持对应功能,还意味着所有适用于相似内置类型的函数,都可以直接拿来用。在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。
理解了这个,就能更好地理解 Python 内置类型的设计规律。