Python数据模型

魔术方法(magic method)是特殊方法的昵称,“双下- getitem”(dunder-getitem)这种说法。于是乎,特殊方法也叫双下方法(dunder method)

一摞Python风格的纸牌

import collections
from random import choice
#这个模块实现了特定目标的容器
Card = collections.namedtuple('Card',['rank','suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2,11)] + list("JQKA") #['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A']
    suits = 'spades diamonds clubs hearts'.split() #['spades', 'diamonds', 'clubs', 'hearts']

    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]

    beer_card = Card('7','diamonds')
    print(beer_card) #Card(rank='7', suit='diamonds')

deck = FrenchDeck()
print(len(deck)) #52

print(deck[0]) #Card(rank='2', suit='spades')

print(choice(deck))

for card  in deck:
    print(card)

如何使用特殊方法

特殊方法的存在是为了被Python解释器调用的,你自己并不需要调用它们。也就是说没有my_object.__len__()这种写法,而应该使用len(my_object)

特殊方法的调用是隐式的,比如for i in x:这个语句,背后其实用的是iter(x),而这个函数的背后则是x.__iter__()方法。

模拟数值类型

一个简单的二维向量类

from math import hypot

class Vector:

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

    # 实现__repr__方法,在控制台打印向量时会输出Vector(1, 2)
    def __repr__(self):
        return 'Vector(%r, %r)' % (self.x, self.y)

    # 返回向量的模
    # hypot()返回欧几里德范数 sqrt(x*x + y*y)
    def __abs__(self):
        return hypot(self.x, self.y)

    # 真假值,如果向量模为0,返回false
    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)

    # 实现向量乘法,例如r*3
    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)


v = Vector(2, 3)
v1 = Vector(6, 8)

print(abs(v1*3))
print(repr(v1))
print(v+v1*2)

字符串表示形式

__repr____str__的区别在于,后者是在str()函数被使用,或是在用print函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更友好。

如果你只想实现这两个特殊方法中的一个,__repr__是更好的选择,因为如果一个对象没有__str__函数,而Python又需要调用它的时候,解释器会用__repr__作为替代。

特殊方法一览

跟运算符无关的特殊方法

跟运算符无关的特殊方法

跟运算符相关的特殊方法

跟运算符相关的特殊方法

为什么len不是普通方法

如果x是一个内置类型的实例,那么len(x)的速度会非常快。背后的原因是CPython会直接从一个C结构体里读取对象的长度,完全不会调用任何方法。获取一个集合中元素的数量是一个很常见的操作,在str、list、memoryview等类型上,这个操作必须高效。


本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!