形如__xxx__
的变量或者函数名就要注意,这些在Python中是有特殊用途的。如__len__()
方法是为了能让class作用于len()
函数。
除此之外,Python的class中还有许多这样有特殊用途的函数,可以帮助我们定制类。
1、字符串定制__str__
首先顶一个一个Person 类,通过输出一个实例引入定制类的问题
#Coding = utf- 8 class Person(object): def __init__(self,name): self.name = name print Person("rhx")
输出结果:<__main__.RoundFloatManual object at 0x00000000056A24E0>
其实本来是想输出实例化对象的,但是输出的确实地址信息,如何才能按照想要输出的形式显示呢?
此时就需要使用 __str__方法,返回一个字符串
#Coding = utf- 8
class Person(object):
def __init__(self,name):
self.name = name
def __str__(self):
return "Person object (name: %s)" %self.name
print Person("rhx")
输出结果:Person object (name: rhx)
但是知道,实例化对象时如果没有把这个实例对象保存到一个变量中,它很快就没有了,会被自动垃圾收集器回收,因为没有任何引用指向这个实例,所以对于Person('rhx')实例化对象,如果没有赋值给一个变量,实例创建时会给其分配一块内存,随即会释放掉它。在python命令行中如果直接调用实例化对象的变量,却也得不到想要的输出结果
>>> class Person(object): ... def __init__(self,name): ... self.name = name ... def __str__(self): ... return "Person object (name: %s)" %self.name ... >>> p = Person("rhx") >>> p <__main__.Person object at 0x0000000004A829E8>
注意这里是在Python(command line),而第一个部分的IDE是pycharm.输出结果也是一个地址值,那如何得到期望的结果呢?
这是因为直接显示变量调用的不是 __str__() 而是 __repr__(),两者的区别是 __str__ 返回的是用户可见的字符串,而 __repr__ 返回的是开发者看到的字符串,也就是说 __repr__ 是为提调试服务的,即是在解释器中转储对象时,显示的是默认对象符号,因此如果要想修复它,只需要覆盖__repr__(),一个简单的办法是把__str__()的实现代码复制给__repr__(),但是要是__str_的实现方法存在问题,那么同样会把该bug带给__repr__,由于python中一切皆对象,因此可以将__str__()这个对象的引用赋值给__repr__,因此有:
>>> class Person(object): ... def __init__(self,name): ... self.name = name ... def __str__(self): ... return "Person object (name: %s)" %self.name ... __repr__= __str__ ... >>> p = Person("rhx") >>> p Person object (name: rhx)
2、数值定制__add__
在字符串定制中,可以通过__str__进行有意义的输出显示,那对于类中的对象如何实现加法呢
class Time(object): def __init__(self,hr,min): self.hour = hr self.minute = min def __str__(self): return '%d:%d' % (self.hour,self.minute) __repr__ = __str__ mon = Time(10,20) tue = Time(9,35) print mon print tue print mon + tue
输出结果: 10:20 Traceback (most recent call last): 9:35 File "F:/py_test/Python Advanced/RoundFloat2.py", line 25, in <module> print mon + tue TypeError: unsupported operand type(s) for +: 'Time' and 'Time'
对象实例化的时候可以正常显示,但是无法运行加法运算。因此需要对其进行重载,重载较为简单,如+,只需要重载__add__()方法,那如何处理这个总数,因为相加之后是一个新的Time对象,并没有对mon和tue对象进行修改,因此需要创建一个新的对象并填入计算出来的总数
def __add__(self, other): return self.__class__(self.hour+other.hour,self.minute+other.minute)
和普通情况一下,新的对象是通过调用类来实现的,不过这里不是使用的Time类,而是使用__class__,在类中,一般不直接使用类名,而是使用self的__class__属性,实例化self的那个类,并调用它。
此外还希望实现“原位”操作,如 += 的形式,在原来变量的基础上增加一个量,即是原位加法, __iadd__(),用来支持想 mon += tue的加法形式,重载一个 __i*__()方法额唯一秘密在它必须返回self,这样不难理解,在原来对象的基础上进行增量加法
def __iadd__(self, other): self.hour += self.hour + other.hour self.minute += self.minute + other.minute
3、__iter__
迭代器就是有一个next()方法的对象,而不是通过索引来计数,当一个循环机制(例如for...)需要下一项时,调用迭代器的next()方法就可以获得它,条目全部获取完之后,引发一个StopIteration异常,告诉外部调用者,迭代完成,对于如何创建一个迭代器呢,对于一个对象调用iter()就可以得到它的迭代器。如何在类中实现
class Fib(object): def __init__(self): self.a,self.b = 0,1 #菲波那切数列的前两项 def __iter__(self): return self def next(self): self.a ,self.b = self.b,self.a + self.b #数列迭代 if self.b >= 5000: raise StopIteration return self.a for n in Fib(): print n
4、元素获取__getitem__
虽然在上面实现了Fib类的迭代,使其用起来想list,但是要像list一样获取一个元素的时候,是否可以呢?
print "The second number of Fib: %d" %Fib()[7]
输出结果: print "The second number of Fib: %d" %Fib()[7] TypeError: 'Fib' object does not support indexing
因此如果想要像list那样按照下表进行访问的话,需要实现 __item__()方法
def __getitem__(self, item): a,b = 1,1 for i in range(item): a,b = b,a + b return a这样就可以进行切片操作了