
这真的是一个毫无用处的类,那么让我们给他添加一个成员函数吧:
你也许要问为什么有个参数叫self。嗯,这是python类中普通成员函数的特点。每一个普通成员函数的第一个参数都代表着调用这个函数的实例本身,也就是说,每个成员函数在定义的时候都必须至少有一个参数,用来传递实例。当然你也可以把它换成其它名字,me,instance之类随便,用self只是取其字面意思。你只要知道第一个参数代表的是调用函数的实例本身就够了。比如我们这样调用这个成员函数:
test = Test()
test.MemberFunction()
你会发现没有了self,是的,在调用的时候不需要再写self,因为self就是test,实际上在调用的时候python将其转为:
Test.MemberFunction(test)
也就是将test传给了self。我们将得到如下结果:
this is a test member function in __main__.Test
这仅仅是个连构造函数都没有的类,而构造,析构函数几乎是一个面向对象语言必需的特性。让我们来看看它们在Python里是什么样子的:
这就是python的构造函数及析构函数(类似析构函数)。然后我们定义一个对象,并引用它的属性x,完了删除test实例
test = Test()
test.x
del test
我们将得到
9
I am dying
几乎没有什么surprise(注意:这里del只是把实例的引用计数减一,并不一定会调用__del__,只有引用计数真正到0的时候才会调用)。就像其它动态语言支持的一样,在python里面我们可以随意添加删除一个普通类实例的成员和属性。比如test.y = 9,那么test实例中将加入y这个属性。实际上在python里面,类的属性查找是用一个字典__dict__来保存的,也就是之前说过的dictionary。察看前面的test实例的__dict__将得到
{'x':9},是不是很方便?当然你也可以用dir(test)获得更多信息。
说到类成员,就不得不说Python类中的自定义操作,类似于操作符重载的机制。这里简单介绍一些基本的重载函数(记住其实是重写,python里面没有函数重载,就算是参数值不同的两个同名函数,也将被重写):
__new__(cls[,...]): 只用于new style class,一般用于生成一个cls的实例,然后返回给__init__使用,多数时候用于继承体系中。
__init__(self[,...]): 前面说过,构造函数
__del__(self): 类似析构函数
__repr__(self):在实例被用作内置函数repr的参数的时候调用,一般用于返回一个对象的"official string representation",就是说在适当环境下,可以用这个返回的string重新构造出一个具有相同值的python对象来。如果不能办到的话,应该返回一些有用的实例信息。
__str__(self):被内置函数str()调用,或者用于print函数,一般返回一个非正式的字符串,相对于repr来说。
__lt__(self, other):less than
__le__(self,other):less equal
__eq__(self,other):equal
__ne__(self,other):not equal
__gt__(self,other):greater than
__ge__(self,other):greater equal
以上6个是几个基本的比较操作符重载,这和C++中差不多,要注意的是它们之间并不存在任何隐式的内在联系,就是说A.__le__(B)为True不代 表A.__gt__(B)为False。所以为了让你的比较能够顺利进行,在你重载一个的同时,你应该将其对应的操作符也重载。你不一定要为这些函数返回 bool值,但是最终都会被转为bool。
__cmp__(self, other):如果以上六个操作符未被重载而被调用,那么__cmp__将作为第二个选择被调用。
__hash__(self):用于字典的key操作。就是说当对象作为dict的key时需要实现的函数。也可以用于被内置函数hash()调用。应该 返回一个32位的int值作为对象的唯一标识。要注意的是,两个对象__cmp__如果相等,那么它们的hash也应该相等。如果一个对象没有定义 __cmp__,那么它也不应该支持__hash__,如果只定义__cmp__而没有__hash__,对象还是不能用作一个字典的key。如果对象内 部定义了可变的对象并且实现了__cmp__,__eq__,那么也不应该实现__hash__,因为这将使字典的key可变,那是不对的。他们的关系比
较暧昧,值得考虑周到。
__nonzero__(self):用于对象的真值测试,也就是bool()函数。应该返回bool值。如果没有实现__nonzero__,那么__len__将作为备选调用,如果都没有实现,那么这个类的所有实例都被认为是True。
__getattr__(self, name):用于获取一个属性值,name就是所需属性的名字,相当于调用test.name。要注意的是,__getattr__只是在正常渠道没有找到属性时的备选函数,比如__dict__中未找到相关属性。在new style class中,有一个__getattribute__,可以完全控制对象属性的获取。
__setattr__(self, name, value):用于设定一个属性值,name是属性名字,value是要设定的值。相当于test.name = value。在旧式类型中,这相当于test.__dict__[name] = value。
__delattr__(self, name):用于从对象实例中删除一个属性。
__get__(self, instance, owner)
__set__(self, instance, value)
__delete__(self, instance)
以上三者只能用在new style class中, 他们其实是用于当一个类的实例出现在其他类的dict中时,对他们的取值赋值操作的自定义。比如:
class TestDescriptor(object):
def __get__(self, obj, type=None):
print "get", self, obj, type
return self.data
def __set__(self, obj, value):
print "set", self, obj, value
self.data = value
def __delete__(self, obj):
print "delete", self, obj
del self.data
class UseDescriptor(object):
desc = TestDescriptor()
我们这样用他们
>>> usedesc = UseDescriptor()
>>> usedesc = UseDescriptor()
>>> usedesc.desc = 5
set <__main__.TestDescriptor object at 0x00DC8D50> <__main__.UseDescriptor object at 0x00DC8610> 5
>>>
usedesc.desc
get <__main__.TestDescriptor object at 0x00DC8D50> <__main__.UseDescriptor object at 0x00DC8610> <class '__main__.UseDescriptor'>
5
5
>>> del usedesc.desc
delete <__main__.TestDescriptor object at 0x00DC8D50> <__main__.UseDescriptor object at 0x00DC8610>
delete <__main__.TestDescriptor object at 0x00DC8D50> <__main__.UseDescriptor object at 0x00DC8610>
一切便都明了:),当然你也可以试试不只是一个obj的情况,即对不同的对象获取不同的属性值。可以在TestDescriptor里面用一个dictionary,用obj作为下标来获取对应的值。
与此相对应的是用于包装类属性操作的property。类似于C#的attribute(仅用于new style class)。
something like this:
class TestProperty(object):
简单地说,就是把__x包装一下,变成属性x,对x的操作实际都通过getx,setx等作用于__x身上。其中property的四个参数分别对应getter, setter, deleter和__doc__。实际的函数名字任取。是不是很好玩?:)
有时候你并不想你的类实例属性被动态增删,这时你可以考虑使用(new style class only):__slots__
一旦定义了这个属性,类实例的属性将固定为__slots__中的值,不能再被增删。
你可以这样用:
class TestSlots(object):
__slots__ = ('only_me')
def __getattr__(self, name):
return "oh it's %s" % name
>>> testslot = TestSlots()
>>> testslot.x = 5
Traceback (most recent call last):
AttributeError: 'TestSlots' object has no attribute 'x'
>>> testslot.only_me
"oh it's only_me"
>>> testslot.only_me = 5
>>> testslot.only_me
5
是不是很神奇?
注意一旦有了__slots__,__dict__将不复存在。不然就自相矛盾了不是吗 :)
我曾想总结一下所有能自定义的操作并列出来,发现实在是太多,无奈水平有限,只能将基本的陈列一下了(注意现在说的很多都只是python里面的classic class,即2.2版之前的所有user-defined class,然而2.2之后的new style class以后将占主导地位,应该更加重视,据说在3.0之后将完全取代old style class,后面将稍有介绍)。
接下来说说python的继承机制,在这之前,先说一个肯定会让你感到惊讶的特性,那就是python类的私有成员,你猜你将如何指定一个成员为私 有?private关键字?不,python里面没有这么一个关键字。Python的私有成员完全取决于它的命名:以__开头,不以__结尾的都被认为是 私有成员。是不是很奇怪?至少我觉得。所以对于大多数可以重写的函数__init__,__del__,__doc__等,都是共有的,但是不建议直接调 用。
(未完)