3.4 getattribute()方法
getattribute()方法提供了对属性更底层的一些操作。默认的实现逻辑是先从内部的dict(或slots)中查找已有的属性。如果属性没有找到则调用getattr()函数。如果值是一个修饰符(参见3.5“创建修饰符”),对修饰符进行处理。否则,返回当前值即可。
通过重写这个方法,可以达到以下目的。
- 可以有效阻止属性访问。在这个方法中,抛出异常而非返回值。相比于在代码中仅仅使用下划线(_)为开头来把一个名字标记为私有的方式,这种方法使得属性的封装更透彻。
- 可仿照getattr()函数的工作方式来创建新属性。在这种情况下,可以绕过getattribute()的实现逻辑。
- 可以使得属性执行单独或不同的任务。但这样会降低程序的可读性和可维护性,这是个很糟糕的想法。
- 可以改变修饰符的行为。虽然技术上可行,改变修饰符的行为却是个糟糕的想法。
当实现getattribute()方法时,将阻止任何内部属性访问函数体,这一点很重要。如果试图获取self.name的值,会导致无限递归。
| getattribute () 函数不能包含任何self.name属性的访问,因为会导致无限递归。 |
为了获得getattribute()方法中的属性值,必须显式调用object基类中的方法,像如下代码这样。
object.getattribute(self, name)
可以通过使用getattribute()方法阻止对内部dict属性的访问来实现不可变。以下代码中的类定义隐藏了所有名称以下划线(_)为开头的属性。
class BlackJackCard3:
"""Abstract Superclass"""
def init( self, rank, suit, hard, soft ):
super().setattr( 'rank', rank )
super().setattr( 'suit', suit )
super().setattr( 'hard', hard )
super().setattr( 'soft', soft )
def setattr( self, name, value ):
if name in self.dict:
raise AttributeError( "Cannot set {name}".
format(name=name) )
raise AttributeError( "'{class.name}' has no attribute
'{name}'".format( class= self.class, name= name ) )
def getattribute( self, name ):
if name.startswith(''): raise AttributeError
return object._getattribute( self, name )
以上代码重写了getattribute()的方法逻辑,当访问私有名称或Python内部名称时代码会抛出异常。和之前的例子相比,这样做的其中一个好处是:对象被封装得更彻底了,我们完全无法改变。
以下代码演示了和这个类的交互过程。
>>> c = BlackJackCard3( 'A', '♠', 1, 11 )
>>> c.rank= 12
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 9, in setattr
File "<stdin>", line 13, in getattribute
AttributeError
>>> c.dict['rank']= 12
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 13, in getattribute
AttributeError
一般情况下,不会轻易使用getattribute()。该函数的默认实现非常复杂,大多数情况下,使用特性或改变getattr()函数的行为就足以满足需求了。
