像我一样,如果你是从一个面向对象语言转而学习Python的,你对下边的语句肯定感到不适:


a = [1,2,3]
len(a)
#为什么不是a.len()?

这其中的原因,简单的说是因为list类里有一个

__len__

方法,当解释器发现

len(object)

,就会自动调用

object.__len__()

方法。
更深一步来说呢,这就是Python的数据模型,它通过一些像

__len__

一样的双下划綫方法来定义了自己语言的数据模型。并且这种数据模型是开放的,你自己的类也可以实现这些方法,来让解释器自动调用。我们自己来定义一个类试一下:


class Team(object):
    def __init__(self):
        self.memberList = []
    def add(self,name):
        self.memberList.append(name)
    def __len__(self):
        return len(self.memberList)
myTeam = Team()
len(myTeam) #输出为0
myTeam.add('Mark')
len(myTeam) #输出为1

可以看出我们自定义的Team类的实例也可以和系统的len方法配合使用了。你也许对此嗤之以鼻,觉得这是些小伎俩。但是你会逐渐理解和喜欢上这种pythonic(python风格的)调用方式。
1. 因为规范了操作,让你不必像java对象调用时纠结object.length() 或者 object.size()。
2. 因为这种规范在很多地方都有用,并且互相配合,使得你只需定义一些原子操作,这些原子操作配合生成的复杂操作则有系统完成,(后边我们会举例子)。
你可以在以下方面让你的对象和Python的数据模型进行交互:
1. 迭代
2. 集合类
3. 属性访问
4. 运算符重载
5. 函数和方法调用
6. 对象的创建和销毁
7. 字符串表示形式和格式化
8. 管理上下文(with块)

我们举一个稍微复杂的例子,我们实现没有没有大小王的扑克类:


class Poker(object):
    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]

我们试试我们定义的这些双下划线函数:


poker = Poker()
len(poker) # 52
poker[2] # Card(rank='4', suit='spades')

目前看起来还不错,不过也没什么神奇的。那我们接着看,如果我们要实现一个随机抽取一张扑克的功能,怎么办?我们的思想是
1. 得到扑克的总张数 index = len(poker)
2. 然后取一个小于index的随机整数,从poker中获取 poker[randombelow(index)]

这个思想用到了我们定义的两个双下划线函数

__len__ 和 __getitem__

。 因为我们严格按照了python数据模型实现了这两个函数,给我们带来的一个好处就是我们不需要再写这个随机函数了。系统已经帮我们实现了一个。它就是 random.choice方法。


import random
random.choice(poker) #Card(rank='3', suit='spades'),这个结果是随机的,你的输出可能和我的不一样。

如果你好奇,可以看一下Python的源码对于random.choice是如何实现的:


def choice(self, seq):
        """Choose a random element from a non-empty sequence."""
        try:
            i = self._randbelow(len(seq))
        except ValueError:
            raise IndexError('Cannot choose from an empty sequence') from None
        return seq[i]

通过这个例子可以看出来遵循python的数据模型,可以帮我们少写很多通用的代码。所以我们也可以把python的数据模型理解为python对所有对象定义的一个接口。并且系统基于这些接口实现了更高级的功能。我们的类不需要显式的声明就可以实现这些接口,从而接入到python的数据模型里。

我们来看更多的例子,我们只是实现了getitem的双下划线方法,我们创建的对象就自动可以迭代了,并且可以切片(slicing)。


for card in poker:
    print(card)
for card in poker[12::13]:
    print(card)

以及判断一张扑克是否存在


Card('Q', 'hearts') in deck

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: