美文网首页
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