5.1.9. 内置的object类
内置的object类是所有内置类型与新型处定义类的最顶层基类。object类定义了一些用来实现默认对象语义的专有方法(请参见“专有方法”章节)。
__new__与__init__
可以通过无参数调用object()来创建一个object类的实例。这会隐式调用object.__new__与object.__init__方法来创建并返回一个没有任何定制属性(即由__dict__所引用的属性)的object类实例。
__delattr__与__getattribute__与__setattr__
默认情况下,使用这些方法来处理属性引用。
__hash__ 与__repr__ 与__str__
object的子类可以按需要覆盖这些方法或添加新的方法。
5.1.10. 类方法
Python通过两个内建的非覆盖描述符类型,为Class提供了两种特殊的“类方法” 。
5.1.10.1. 静态方法
静态方法就是可以从类或类的任一实例上进行调用的方法,同时不需要应用在调用普通方法、约束或非约束方法时,对于第一个特定调用参数的约束条件。静态方法可以有任意的签名格式,可以没有任何参数,而它的第一个参数也不需要扮演任何特殊的角色。你可以不用理会静态方法由类的属性进行界定的事实,将它想象成就是一个普通的可以调用的函数。从这个角度看,我们完全可以通过定义一个普通的函数来代替定义一个静态方法,但是当一个函数的目标与一个特定的类有很强的约束关系时,静态方法就可以提供一个优雅的实现。
要创建一个静态方法,调用内建类型staticmethod,并将它的返回值绑定到一个类属性上。与其它类属性的绑定类似,静态方法的绑定通常也在类的主体部分内完成,当然也可以在其它地方来进行绑定处理。staticmethod的唯一参数就是调用静态方法时要执行的那个方法对象。下面的例子说明了如何创建与调用静态方法:
class AClass(object):
def astatic( ): print 'a static method'
astatic = staticmethod(astatic)
anInstance = AClass( )
AClass.astatic( )
# prints: a static method
anInstance.astatic( )
# prints: a static method
在示例中,对于传递给staticmethod的函数名与表示静态方法的类属性使用了相同的名字,这种风格不是必需的,但我们建议这样做,以提高代码的可读性。
在Python2.4中,有一种新的简化的语法来支持它,请参见“装饰符(Decorator)” 部分章节。
5.1.10.2. 类方法
类方法可以从类对象或类的任一实例对象进行调用。对于从类对象进行调用的情况,Python将此类作为第一个参数隐式传入此类方法;对于从实例对象进行调用的情况,Python将此实例的类作为第一个参数隐式传入此类方法。与普通方法不同,在调用此它传入的第一个参数不是实例对象,而是类对象。类方法的第一个参数名字通常都定义为cls,对应的普通方法的第一个参数的习惯命名为self。当然可以通过定义一个将类对象作为第一个参数的普通函数来代替类方法,但是在一些场合下,使用类方法是更优雅的实现方法。
要创建一个类方法,调用内建类型classmethod,并将它的返回值与一个类属性进行绑定就行了。classmethod的唯一参数就是Python调用类方法时所执行的函数对象。
class ABase(object):
def aclassmet(cls): print 'a class method for', cls._ _name_ _
aclassmet = classmethod(aclassmet)
class ADeriv(ABase): pass
bInstance = ABase( )
dInstance = ADeriv( )
ABase.aclassmet( )
# prints: a class method for ABase
bInstance.aclassmet( )
# prints: a class method for ABase
ADeriv.aclassmet( )
# prints: a class method for ADeriv
dInstance.aclassmet( ) # prints: a class method for ADeriv
在示例中,函数对象与类方法使用了相同的名字,这种风格不是必需的,但建议总是这样做,以提高代码可读性。
在Python2.4中,有一种新的简化的语法来支持它,请参见“装饰符(Decorator)” 部分章节。
5.1.11. Property
Python通过内建的覆盖描述符类型,来为类实例提供属性。
Property也是拥有特设的功能的实例对象属性,也一样可以通过普通的语法进行引用,绑定或解除绑定的操作(print x.prop, x.prop=23, del x.prop)。然而与普通的实例属性不同,对于实例Property的存取调用操作,都将会执行你在声明Property时作为参数传入的实例方法。下面的示例是一个只读属性:
class Rectangle(object):
def _ _init_ _(self, width, height):
self.width = width
self.height = height
def getArea(self):
return self.width * self.height
area = property(getArea, doc='area of the rectangle')
类Rectangle的每个实例r都拥有一个人为组装出来的只读属性r.area,它由r.getArea()方法进行运算得到,在属性定义中还提供了它的docstring。属性r.area是只读的,不能重绑定与解除绑定,因为我们只提供了一个get方法,而没有提供set与del方法。
Property的内部处理与专有方法__getattr__,__setattr__,以及__delattr__类似,只是它更快更简单。通过调用内建类型property,并将它的返回值绑定到一个类属性上,就可以创建一个Property。与所有其它类属性一样,一般也都在类声明体内完成定义,当然也可以在其它地方进行处理。对于类C,使用如下的声明语法:
attrib = property(fget=None, fset=None, fdel=None, doc=None)
若x为C的一个实例,当你引用x.attrib时,Python会不带参数调用在你在property调用时指定的fget方法。当你为属性批派值时,如x.attrib = value,Python会将value作为唯一的参数调用fset方法。当你执行del x.attrib时,Python将不带参数调用fdel方法。当然,Python会将你提供的doc设置为此属性的docstring。调用property时的所有参数都是可选的。若没有提供对应的方法,则对应的操作就是禁止的(当你想执行此类操作时,Python将会抛出异常)。如在Rectangle示例上,area是只读的,因为我们只提供了fset参数。
5.1.11.1. Property的重要性
Property的重要性就在于当你在类的公共接口中暴露一个公共数据属性时,它提供了一个完全安全且真正可取的方式。通过Property,当属性在被引用,重绑定或解除绑定时,将会执行相应的方法,这样,在类的未来版本中,如果有需要,你可以将普通属性修改成为Property属性,而不需要修改任何客户端代码。这样我们根本不需要任何愚蠢的模式,如存取器或修改器方法,而在没有Property语法支持或类似机制的语言中,这些愚蠢方法都是必须要使用的。如,客户端代码只需要简单地使用此属性:
someInstance.widgetCounter += 1
而不是调用get方法与set方法来实现:
someInstance.setWidgetCounter(someInstance.getWidgetCounter( ) + 1)
所以任何时候,你想写出getXXX或setXXX代码的时候,请使用Property来代替它们。
5.1.11.2. Property与继承
Property是可以与其它属性一样被继承的。但是这里有个陷井需要小心对待:在存取属性时将会调用的方法,只可能是定义它的那个类中的方法,对于这些方法,不可能通过子类覆盖来修改它的行卫。如:
class B(object):
def f(self): return 23
g = property(f)
class C(B):
def f(self): return 42
c = C( )
print c.g
# prints 23, not 42
对于c.g的读取只会调用B.f而不是C.f。原因很简单,取决于在Property被创建时,我们传入的函数对象f。事实是,在子类C中,属性名f被重定义了,但这于Property是完全不相关的,Property只会使用在创建它时所传入的函数对象。当然,如果你想绕开这一点,也可以如下:
class B(object):
def f(self): return 23
def _f_getter(self): return self.f( )
g = property(_f_getter)
class C(B):
def f(self): return 42
c = C( )
print c.g
# prints 42, as expected
在上例中,与Property相关的函数对象是B._f_getter,在此函数的运行中,它会去查找名字为f的方法,所以在这里,对于f的覆盖是有效的。
5.1.12. __slots__
一般来说,类C的每个实例都有一个字典x.__dict__,Python利用它来存储x的任意属性。为了节省一点内存(即让x拥有一个预定义好的属性名的集合),可以在新型C中定义一个类属性__slots__,它是一个字符序列(通常是一个tuple)。当一个新型类C拥有类属性__slots__时,类C的直接实例x将不拥有__dict__属性,任何想为x绑定一个新的且名字不在C.__slots__序列中的属性都将会引发一个异常。
class OptimizedRectangle(Rectangle):
_ _slots_ _ = 'width', 'height'
OptimizedRectangle类的实例将只能拥有属性width与height。注意,对于Property属性area,我们不需要进行任何限定,因为__slots__只对于那些会存储在__dict__字典中的普通属性有效。
5.1.13. __getattribute__
对于新型类的实例,所有属性引用操作都是由专有方法__getattribute__定义的。它由蕨类object提供,在方法中定义了如前面章节描述的属性查找机制与语义。
然而,你可以覆盖__getattribute__方法,来实现你的特定目的,如在子类实例中,隐藏继承的类属性或方法。下面的示例,说明了一个禁止List的append操作的方法:
class listNoAppend(list):
def _ _getattribute_ _(self, name):
if name == 'append': raise AttributeError, name
return list._ _getattribute_ _(self, name)
类listNoAppend的实例x与内建的list对象一样,只性能会差一点,且不支持x.append方法。
5.1.14. Per-Instance Methods
Both the legacy and new-style object models allow an instance to have instance-specific bindings for all attributes, including callable attributes (methods). For a method, just like for any other attribute (except those bound to overriding descriptors in new-style classes), an instance-specific binding hides a class-level binding: attribute lookup does not consider the class when it finds a binding directly in the instance. In both object models, an instance-specific binding for a callable attribute does not perform any of the transformations detailed in "Bound and Unbound Methods" on page 91. In other words, the attribute reference returns exactly the same callable object that was earlier bound directly to the instance attribute.
Legacy and new-style object models do differ on the effects of per-instance bindings of the special methods that Python invokes implicitly as a result of various operations, as covered in "Special Methods" on page 104. In the classic object model, an instance may usefully override a special method, and Python uses the per-instance binding even when invoking the method implicitly. In the new-style object model, implicit use of special methods always relies on the class-level binding of the special method, if any. The following code shows this difference between the legacy and new-style object models:
def fakeGetItem(idx): return idx
class Classic: pass
c = Classic( )
c.__getitem__ = fakeGetItem
print c[23] # prints: 23
class NewStyle(object): pass
n = NewStyle( )
n.__getitem__ = fakeGetItem
print n[23] # results in:
# Traceback (most recent call last):
# File "<stdin>", line 1, in ?
# TypeError: unindexable object
The semantics of the classic object model in this regard are sometimes handy for tricky and somewhat obscure purposes. However, the new-style object model's approach is more general, and it regularizes and simplifies the relationship between classes and metaclasses, covered in "Metaclasses" on page 116.
5.1.15. 从内置类型进行继承
可以从内建类型继承得到一个新型子类。然而,只有这些内建类型被设计为可以彼此兼容,才可以使用多继承机制从多个内建类型继承得到子类。对于任意的内建类型,Python不支持无约束的多重继承。通常情况下,新型类至多只可以从一个具有真正价值的内建类进行继承,因为所有的新型类已经是object的子类。如:
class noway(dict, list): pass
将会引发一个TypeError异常,异常的信息为“Error when calling the metaclass bases: multiple bases have instance lay-out conflict." 。若你看到这条异常信息,说明你尝试直接或间接地从多个不能协同工作的内建类型进行继承操作。