fluent python 读书笔记 1

学习python一段时间会发现,一直都是用python做业务逻辑。每次都是为了解决问题,而解决问题。而python中丰富的库会让我们欣喜,但是也可能让我们变懒,真正对python的理解却没有增加多少。而fluent python是进阶python非常好的一本书。

Python的数据模型

Let's be pythonic!

# 例子来自fluent python第4页
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, item):
        return self._cards[item]复制代码

有了这些方法以后,我们就可以像正常操作列表一下操作我们的 FrenchDeck 。

deck = FrenchDeck()
len(deck) #输出为52复制代码

由于提供了 __getitem__ 方法,可以直接通过下标直接访问对应的位置的元素,甚至可以像列表那样进行切片操作和遍历操作。

deck[0] # Card(rank='2', suit='spades')
deck[1] # Card(rank='A', suit='hearts')
deck[0:3] # [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
for card in deck:
    print card  # 输出省略复制代码

需要指出的是遍历操作并不总是显式的。如果一个集合没有实现 __contains__ 方法,则 in 操作就会进行顺序遍历操作。

至于排序操作,需要我们提供排序的依据,现在假设排序的是按照先看号码,再看花色的顺序,其中花色按照梅花,方块,红桃,黑桃的顺序。所以可以按照这个顺序,计算出每张牌的位置索引,如下:

suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
def spades_high(card):
    rank_value = FrenchDeck.ranks.index(card.rank)
    return rank_value * len(suit_values) + suit_values[card.suit]复制代码

然后就可以进行排序操作了:

for card in sorted(deck, key=spades_high):
    print card
# 输出
Card(rank='2', suit='clubs')
Card(rank='2', suit='diamonds')
Card(rank='2', suit='hearts')
... (46 cards ommitted)
Card(rank='A', suit='diamonds')
Card(rank='A', suit='hearts')
Card(rank='A', suit='spades')复制代码

通过实现 __len____getitem__ 方法,我们就可以像操作 python 内置的数据类型那样操作我们的数据模型。不过两者依然有区别。在 CPython 中,当我们对内置的数据类型进行 len(x) 操作的时候,我们其实并没有调用任何方法,只是取得 C 结构中的一个域。这使得我们可以高效的操作很多内置类型,如 str, list, memoryview 等等。

模拟数学类型

我们都在高中学过矢量运算。

矢量操作

基本的矢量操作包括,矢量相加,矢量求模,矢量和标量相乘等等。然而我们希望用我们习惯的内置操作 + abs *来进行这些运算,所以需要我们自定义的数据模型实现一些特殊方法。

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)复制代码

实现了这些特殊方法后,就可以直接使用运算符对我们的数据模型进行操作了

v1 = Vector(2,4)
v2 = Vector(2,1)
v1 + v2 # Vector(4,5)
v = Vector(3,4)
abs(v) # 5.0
v * 3 # Vector(9, 12)
abs(v * 3) # 15.0复制代码

值得指出的是 __repr__ 方法,如果不实现这个方法的话。直接打印一个 Vector 对象可能会输出

0x10e100070>.复制代码

而定义了这个方法后,输出就变得相当易读

Vector(3,4)复制代码

当然,还可以定义 __str__ 来自定义str(x)的行为。如果只想实现一个函数的话,建议实现 __repr__ 函数,因为当没有 __str__,会调用__repr__作为备用。

转载于:https://juejin.im/post/58dfd75aa0bb9f0069e6ef03

你可能感兴趣的:(python)