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()函数的行为就足以满足需求了。