Python Data Model @ 流畅的Python 01

最近时常感觉有必要重学一遍python,故开此坑。
《流畅的python》这本书也是经典必读了,就从第一章开始写笔记。这章是一个引言,主要介绍了python的special methods。

Special Methods

  • Special Method是形如__methodName__的一类方法(也叫做”dunder method”,dunder是双下划线的略称),通过实现这类方法可以使得class以一些python通用的语法来调用一些特殊函数,使得代码语法更连贯,避免在用户编写的类里同样功能的函数有多种不同名称的情况。(Meant to be called by the python interpreter, not user (the only exception being __init__() which is for invoking the initializer for user superclass))
    • e.g. 通过实现 __getitem__()方法,可以直接通过 className[0], className[1]这样类名加方括号的形式来获取类中按照某种顺序的第N个元素(get item by index)
    • ✴️ __getitem__()还能让我们的类变得iterable
      • 可以用 in 看某个元素是否在里面
      • 可以用 for 遍历
      • 需要sorting的话,可以创建一个函数sort_value()用以得到某种具有顺序的值,最后用for card in sorted(deck, key=sort_value):即可按照这个函数输出的值来排序。
    • e.g. 实现__len__()方法就可用len(class)来获取事先定义好的某种length。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      import collections

      Card = collections.namedtuple('card', ['rank', 'suit'])
      #name 对应console里面的这个tuple的名字
      #>>>Card('2', 'diamonds')
      #output: card(rank='2', suit='diamonds')

      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]

      #iterable
      >>> for card in deck:
      print(card)

      #Card(rank='2', suit='spades')
      #Card(rank='3', suit='spades')
      #Card(rank='4', suit='spades')

  • e.g. 模拟数值类 Emulating Numeric Types
    • To build a vector type Vector(a,b), you’ll have to implement __repr__(), __abs__(), __add__() and __mul__()
  • 书中继续介绍了__repr__()__str__()两个special methods, 一个是展示在console,一个是展示在print里的。
    • __repr__() is called by the repr builtin to display the string representation of the object in the console.
    • With repr, the console displays: Vector(9, 12), instead of <Vector object at 0x10e100070>
    • __str__ is called by the str() built-in and implicitly used by the print function. It should return a string suitable for display to end users.

Collection API

接下来简单介绍了Python的Collection API。所有的类都是ABC(abstract base classes)。

  • 我们有top ABC,这些ABC都只有一个special method,如Iterable的__iter__, Sized的__len__.
    • Interable要求支持unpacking和iteration
    • Sized要求实现len
    • Container要求支持in操作符
  • Collection ABC (即 collections.abc module)合并了Iterable, Sized和Container三个接口。
  • Python不要求每个具体的类都要实现__len__,但如果他实现了,那么他就satisfy 了Sized Interface。
    • 具体关系为
  • 其中,三个重要的Collections是
    • Sequence, 规范了list, str 这些重要的built-in;只有sequence是reversible的,因为它支持arbituary ordering,其他两个不行。dict是假order,只保留key insertion order。
    • Mapping, 有dict, collections.defaultdict等实现
    • Set, 有setfrozenset

其它

BTW, 在CPython里,builtin types的len()其实不是一个method,它只是从C struct里面读取了一个field的值,所以len()效率很高;因为builtin需要高效的实现。现在,用户可以通过实现__len__使得len()同样能用在自己的类上。