Fluent Python 笔记(一):数据模型

1,148 阅读3分钟
原文链接: zhuanlan.zhihu.com

目录:

Fluent Python 笔记(一):数据模型

Fluent Python 笔记(二):序列基础

Fluent Python 笔记(三):高效操作序列

前段时间(年前)撸完了 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)

在程序设计中,鸭子类型(英语:duck typing)是动态类型的一种风格。在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。

理解了这个,就能更好地理解 Python 内置类型的设计规律。

(下一篇:Fluent Python 笔记(二):序列基础