目录

特殊方法

为什么要实现特殊方法

元对象协议


特殊方法

python的特殊方法又称魔术方法,定义的方式是双下划线+方法名:"__[method_name]__",这种方法一般是被python解释器来调用的,调用的时机是在对对象进行某些操作时,例如运算、获取长度等等,例如执行 len(obj)时,如果对象实现了 __len__ 方法,那么python解释器就会调用 __len__ 方法,它的返回值就作为 len(obj) 的返回值。

部分特殊方法有对应的内置函数,例如 len() 和 __len__(),abs() 和 __abs__(),bool() 和 __bool__()等等,当使用这些内置函数对对象进行操作时,就会调用与之对应的特殊方法。

下面设计一个实现了 __init__()、__len__() 和 __getitem__() 的类:

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]

collections.namedtuple()构建一个有两个属性 rank 和 suit、而没有方法的 Card 类。

创建 Card 对象,跟一般的对象创建没有区别:

>>> import collections
>>> Card = collections.namedtuple('Card', ['rank', 'suit'])
>>> first_card = Card(7, 'spades')
>>> first_card
Card(rank=7, suit='spades')

创建一个FrenchDeck对象,然后调用它所实现的特殊方法:

deck = FrenchDeck()
# 调用 __len__()
print(len(deck))            # 52

# 调用 __getitem__()
print(deck[1])              # Card(rank='3', suit='spades')

__getitem__() 是访问对象时调用,它是迭代操作和切片的基础。

为什么要实现特殊方法

这就涉及到 python 的设计理念,python非常实用的两个特性是迭代和操作,我们定义的序列数据类型,例如列表和元组,可以对它们进行循环遍历,以及切片,但如果想让我们自己创建的数据类型(就是),也能使用这些特性,那就需要实现特殊方法。

另外的好处就是可以使用像 len()、sorted() 这些内置函数对我们自定义的数据类型进行操作,换句话说,通过实现特殊方法,使我们创建的数据类型跟原生的数据类型一样,使用统一的 API,而不需要额外实现

下面的例子可以看出这样做的好处:

for card in deck:
    print(card)         # Card(rank='2', suit='spades') Card(rank='3', suit='spades') ...



suit_values = dict(spades=3, hearts=2, diamonds=1, clubs=0)
# 重新定义排序,黑桃>红心>方砖>梅花,即 黑桃2 > 红心2 > 方砖2 > 梅花2 > 黑桃3 > 红心3 > .....
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='spades') Card(rank='3', suit='hearts') ...

元对象协议

在《流行的python》一书中,第一章还介绍了元对象协议,这词很抽象,我的理解是:元对象是内置的、基础的对象,而协议就是一堆特殊方法(接口)的定义,元对象协议就是开放了特殊方法实现的对象定义方式,说白点,就是通过实现特殊方法,来定义与原生对象一样的对象。