如果定义了任何属性访问控制方法,容易产生错误。思考下面这个例子:
def __setattr__(self, name, value): self.name = value # since every time an attribute is assigned, __setattr__() is called, this # is recursion. # so this really means self.__setattr__('name', value). Since the method # keeps calling itself, the recursion goes on forever causing a crashdef __setattr__(self, name, value): self.__dict__[name] = value # assigning to the dict of names in the class # define custom behavior here再次证明了Python的魔法方法是难以置信的强大,但强大的力量也需要强大的责任。如果你不想运行时中断你的代码,那了解如何适当地使用魔法方法就非常重要啦。
我们从Python中定制的属性访问中学到了什么?它们不是被轻易使用的。事实上,它有点过分强大并且违反直觉。但它们存在的原因是用来止痒的:Python不阻止你制造遭糕东西,但可能会让它变的困难。自由是最重要的东西,所以你可做任何你想做的事情。这里有一个例子,展示了一些特殊的属性访问控制行为。(注意我们使用super,因为不是所有的类都有__dict__属性):
class AccessCounter(object): '''A class that contains a value and implements an access counter. The counter increments each time the value is changed.''' def __init__(self, val): super(AccessCounter, self).__setattr__('counter', 0) super(AccessCounter, self).__setattr__('value', val) def __setattr__(self, name, value): if name == 'value': super(AccessCounter, self).__setattr__('counter', self.counter + 1) # Make this unconditional. # If you want to prevent other attributes to be set, raise AttributeError(name) super(AccessCounter, self).__setattr__(name, value) def __delattr__(self, name): if name == 'value': super(AccessCounter, self).__setattr__('counter', self.counter + 1) super(AccessCounter, self).__delattr__(name)] 自定义序列有很多办法能让你的Python类使用起来就像内置的序列(dict,tuple,list,string等)。Python里有一些目前我最喜欢的办法,因为它们给你的控制到了荒谬的程度并且神奇地使得大量的全局函数优雅地工作在你类的实例当中。但是在深入讲这些好东西之前,我们先介绍下需求。
需求在讨论在Python中创建你自己的序列也是时候谈谈协议了。在其他语言中协议有点类似于接口,因为你必须实现一系列的方法。然而,在Python中协议是完全不正式的,不需要显式的声明去实现它,它更像是一种指导原则。
为什么我们要谈论协议呢?因为在Python中实现自定义容器类型涉及到这些协议的使用。首先,有一些协议用于定义不变容器:为了实现一个不变窗口,你只需定义__len__和__getitem__方法(接下来会细说)。不变容器的协议要求所有的类加上一个__setitem__和__delitem__方法。最后,如果你想让你的容器支持遍历,你必须定义__iter__方法,它返回一个iterator。这个iterator必须遵守iterator的协议,它要求iterator类里面有__iter__方法(返回自身)和next方法。