很多从其他语言转向Python的人都抱怨Python的类缺少真正意义上的封装(即没办法定义私有属性然后使用公有的getter和setter)。然而事实并非如此。实际上Python不是通过显式定义的字段和方法修改器,而是通过魔法方法实现了一系列的封装。
__getattr__(self, name)
当用户试图访问一个根本不存在(或者暂时不存在)的属性时,你可以通过这个魔法方法来定义类的行为。这个可以用于捕捉错误的拼写并且给出指引,使用废弃属性时给出警告(如果你愿意,仍然可以计算并且返回该属性),以及灵活地处理AttributeError。只有当试图访问不存在的属性时它才会被调用,所以这不能算是一个真正的封装的办法。
__setattr__(self, name, value)
和 __getattr__ 不同, __setattr__ 可以用于真正意义上的封装。它允许你自定义某个属性的赋值行为,不管这个属性存在与否,也就是说你可以对任意属性的任何变化都定义自己的规则。然后,一定要小心使用 __setattr__ ,这个列表最后的例子中会有所展示。
__delattr__(self, name)
这个魔法方法和 __setattr__ 几乎相同,只不过它是用于处理删除属性时的行为。和 __setattr__ 一样,使用它时也需要多加小心,防止产生无限递归(在 __delattr__ 的实现中调用 del self.name 会导致无限递归)。
__getattribute__(self, name)
` __getattribute__` 看起来和上面那些方法很合得来,但是最好不要使用它。 __getattribute__ 只能用于新式类。在最新版的Python中所有的类都是新式类,在老版Python中你可以通过继承 object 来创建新式类。 __getattribute__ 允许你自定义属性被访问时的行为,它也同样可能遇到无限递归问题(通过调用基类的 __getattribute__ 来避免)。 __getattribute__ 基本上可以替代 __getattr__ 。只有当它被实现,并且显式地被调用,或者产生 AttributeError 时它才被使用。 这个魔法方法可以被使用(毕竟,选择权在你自己),我不推荐你使用它,因为它的使用范围相对有限(通常我们想要在赋值时进行特殊操作,而不是取值时),而且实现这个方法很容易出现Bug。
自定义这些控制属性访问的魔法方法很容易导致问题,考虑下面这个例子:
def __setattr__(self, name. value): self.name = value # 因为每次属性幅值都要调用 __setattr__(),所以这里的实现会导致递归 # 这里的调用实际上是 self.__setattr('name', value)。因为这个方法一直 # 在调用自己,因此递归将持续进行,直到程序崩溃 def __setattr__(self, name, value): self.__dict__[name] = value # 使用 __dict__ 进行赋值 # 定义自定义行为再次重申,Python的魔法方法十分强大,能力越强责任越大,了解如何正确的使用魔法方法更加重要。
到这里,我们对Python中自定义属性存取控制有了什么样的印象?它并不适合轻度的使用。实际上,它有些过分强大,而且违反直觉。然而它之所以存在,是因为一个更大的原则:Python不指望让杜绝坏事发生,而是想办法让做坏事变得困难。自由是至高无上的权利,你真的可以随心所欲。下面的例子展示了实际应用中某些特殊的属性访问方法(注意我们之所以使用 super 是因为不是所有的类都有 __dict__ 属性):
class AccessCounter(object): ''' 一个包含了一个值并且实现了访问计数器的类 每次值的变化都会导致计数器自增''' 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) # 使计数器自增变成不可避免 # 如果你想阻止其他属性的赋值行为 # 产生 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)6. 自定义序列