美文网首页
Fluent Python 读书笔记01 Python数据模型

Fluent Python 读书笔记01 Python数据模型

作者: 红煌流星 | 来源:发表于2018-11-28 23:12 被阅读0次

Chapter 01 The Python Data Model

一叠Python风格的纸牌

先用一个例子来演示__getitem____len__魔法方法。

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]
  • 用nametuple来创建对象的类,不需要用自定义的方法来绑定属性
  • 可以响应len()函数返回卡牌数量(__len__)
>>>deck = French()
>>>len(deck)
52
  • 可以索引指定的牌,这是__getitem__方法提供的
>>>deck[0]
Card(rank="2", suit="spides")
>>>deck[-1]
Card(rank="A", suit="hears")
  • 纸牌也是可以迭代的(__getitem__)
>>>for card in deck():
...     print(card)
Card(rank="A", suit="hearts")
...
  • 因为可以迭代(iterable),解包操作也是支持的(__getitem__)
>>>from random import choice
>>>card1 = choice(deck)
>>>x, y = card1
>>>x, y
"7", "spades"
  • __getitem__方法给slef._cards授权了[]操作,我们的卡牌支持切片操作。
>>>deck[:3]
  • 迭代通常是隐形的。如果一个集合类型没有__contains__方法,那么in操作符会对进行一次序列性扫描(a sequential scan)。(__getitem__)
>>>Card("Q", "hearts") in deck
True

怎样使用魔法方法

魔法方法是Python解释器调用的,而不是通过你。不需要写my_object.__len__()这种写法,写len(my_object),Python会调用__len__

对于内置的类型,比如list, str, bytearray等等,解释器会使用更快捷的方式而不是魔法方法,因为这样更快。比如在CPython的len()的实现实际上是返回在PyVarObjectC结构体里的ob_size字段的值,PyVarObject是内存中任意可变长度的内置对象。
对于len为什么不是一个方法,核心开发者Raymond Hettinger的解释是:practicality beats purity。实用胜于纯粹。获取集合类型的元素数量是一个通用操作而且对于这些基本的类型,需要更加高效。

通常情况下,魔法方法的调用是隐性的。比如,for i in x语句,实际上调用iter(x),在这背后的实现是x.__iter__(),前提是这个方法实现了(前面没有__iter__没有实现而是用__getitem__)

当没有自定义的__str__方法时,会调用__repr__作为替代,所以在__str____repr__方法里只能选择一个的话,选择__repr__

模拟数学类型

再来另一个例子,用一个类来实现二维向量的表示

from math import hypot


class Vector:

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Vector({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)

分析一下:

  • 重载__repr__,当我们使用print的时候,调用的是str(),也就是__str__,这里我们没有重载__str__方法,这样print会调用__repr__
>>>v1 = Vector(2, 4)
>>>v2 = Vector(2, 1)
>>>v1  # 调用__repr__()
Vector(2, 4)
>>>print(v1)  # 没有__str__,调用__repr__()
Vector(2, 4)
  • 重载__add____mul__,这两个方法背后是+, *操作符:
>>>v1 + v2
Vector(4, 5)
>>>v1 * 3
Vector(6,  12)
# 3 * v1和v1 * v2是报错的,具体我还没会
# TypeError: unsupported operand type(s) for *: 'int' and 'Vector'

相关文章

网友评论

      本文标题:Fluent Python 读书笔记01 Python数据模型

      本文链接:https://www.haomeiwen.com/subject/nymgqqtx.html