8、Python-方法、属性、迭代器
1、旧式类和新式类
Python中类的工作方式正在发生变化。在3.0以前的版本中Python内有两种类,旧式类和新式类,新式类在2.2版本中引入,它提供了一些新的特征(比如super函数和property函数,而旧式类中没有)。为了创建新式的类,必须直接或间接子类化object,或者在模块的开始处添加赋值语句__metaclass__=type。
2、方法
2.1 方法分类
- 魔法方法:Python中一些特殊的方法,名字是以双下划线开始和结束,这些方法和类中的其他方法没有什么不同,只是在某些情况下类对象会自动调用这些特殊的方法;
- 构造方法:Python中构造方法被命名为__init__,在对象创建完成后立即执行;
- 析构方法:Python中析构方法被命名为__del__,在对象就要垃圾回收之前调用,由于发生调用的时间不可知,所以要避免使用__del__方法;
2.2 重写方法
一个类能够实现与其超类中定义的方法和属性,从而使其超类中的方法和属性无效。重写是继承机制中的一个重要内容,对于构造方法尤其重要。
1 class A: 2 def hello(self): 3 print "Hello, I'm A." 4 5 class B(A): 6 def hello(self): 7 print "Hello, I'm B."
注意:如果一个类的构造方法被重写,那么就需要调用超类的构造方法,否则对象可能不被正确地初始化。
1 class Bird: 2 def __init__(self): 3 self.hungry = 1 4 def eat(self): 5 if self.hungry: 6 print 'Aaaah...' 7 self.hungry = 0 8 else: 9 print 'No, thanks!' 10 11 class SongBird(Bird): 12 def __init__(self): 13 self.sound = 'Squawk!' 14 def sing(self): 15 print self.sound
SongBird类对象调用eat()方法会报错,原因是该类构造方法中没有对超类初始化。
2.2 构造方法的两种方式
2.2.1 调用超类构造方法的未绑定版本
绑定方法:在调用一个实例的方法时,该方法的self参数被自动绑定到实例上,这样的方法称为绑定方法。
未绑定方法:直接调用类本身的方法时,就没有实例被绑定,这时就需要提供给方法一个self参数,这样的方法称为未绑定方法。
1 class SongBird(Bird): 2 def __init__(self): 3 Bird.__init__(self) 4 self.sound = 'Squawk!' 5 def sing(self): 6 print self.sound
上面的例子中,将类SongBird本身,即self,作为参数传递给超类Bird的未绑定方法__init__(),这样SongBird就能够使用其超类构造方法的所有实现。
2.2.2 使用super函数
将当前的类和对象作为super函数的参数使用,返回的对象可以调用超类中的任何方法,而不是当前类中的方法。
1 class SongBird(Bird): 2 def __init__(self): 3 super(SongBird, self).__init__() 4 self.sound = 'Squawk!' 5 def sing(self): 6 print self.sound
注意:super函数比在超类中直接调用未绑定的方法直观,并且super函数非常智能,即使该类继承了多个超类,它也只需要被调用一次。
3、成员访问
序列和映射是对象的集合。为了实现它们基本的行为,如果对象是不变的,那么就需要使用两个魔法方法,如果是可变的则需要使用4个。
- __len__(self):返回集合中所含项目的数量。
- __getitem__(self, key):返回与所给键对应的值。
- __setitem__(self, key, value):按一定的方式存储和key相关的value。
- __delitem__(self, key):在对一部分对象使用del语句时被调用,同时必须删除和元素相关的键。
注意:对于一个序列,如果键是负值,要从序列的末尾开始计数。x[-n]等价于x[len(x)-n];如果键是不合适类型会引发TypeError异常;如果键超出了范围会引发IndexError异常。
下面示例实现了一个和内建列表行为相似的序列,使用子类list。
1 >>> class CounterList(list): 2 def __init__(self, *args): 3 super(CounterList, self).__init__(*args) 4 self.counter = 0 5 def __getitem__(self, index): 6 self.counter += 1 7 return super(CounterList, self).__getitem__(index) 8 9 >>> c1 = CounterList(range(10)) 10 >>> c1 11 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 12 >>> c1.reverse() 13 >>> c1 14 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 15 >>> del c1[0:2] 16 >>> c1 17 [7, 6, 5, 4, 3, 2, 1, 0] 18 >>> c1.counter 19 0 20 >>> c1[0] 21 7 22 >>> c1.counter 23 1
4、属性
4.1访问器方法
访问器是一个简单的方法,它能够使用get、set这样的名字来得到或者重绑定一些特性。通过访问器创建的特性被称为属性。
property函数创建一个属性,其中访问器函数被当做参数来使用,property函数可以有最多4个参数来调用。property的4个参数分别被叫做fget、fset、fdel和doc。
1 >>> class Rectangle(object): 2 def __init__(self): 3 self.width = 0 4 self.height = 0 5 def setSize(self, size): 6 self.width, self.height = size 7 def getSize(self): 8 return self.width, self.height 9 size = property(getSize, setSize) 10 11 12 >>> r = Rectangle() 13 >>> r.width = 2 14 >>> r.height = 5 15 >>> r.size 16 (2, 5) 17 >>> r.size = 10,20 18 >>> r.size 19 (10, 20)
4.2 静态方法和类成员方法
静态方法和类成员方法在创建时分别被装入Staticmethod类型和Classmethod类型的对象中。静态方法的定义没有self参数,且能够被类本身直接调用。类方法的定义需要名为cls了参数,类似于self,类成员方法可以用类的具体对象调用。
__metaclass__ = type class MyClass: def smeth(): print 'This is a static method' smeth = staticmethod(smeth) def cmeth(cls): print 'This is a class method of', cls cmeth = classmethod(cmeth)
手动的包装用起来不方便,从而引入了装饰器的新语法。使用@操作符,在方法的上方将装饰器列出来,从而指定一个或者更多的装饰器。
__metaclass__ = type class MyClass: @staticmethod def smeth(): print 'This is a static method' @classmethod def cmeth(cls): print 'This is a class method of', cls def normeth(self): print 'This is a normal class method of', self MyClass.smeth() MyClass.cmeth() #MyClass.normeth() 普通方法只支持类的实例化调用 test = MyClass() test.smeth() test.cmeth() 运行结果: This is a static method This is a class method of <class '__main__.MyClass'> This is a static method This is a class method of <class '__main__.MyClass'> This is a normal class method of <__main__.MyClass object at 0x00BB9910> test.normeth()
注意:静态方法和类成员方法在Python中并不是向来都很重要,主要的原因是大部分情况下可以用函数或者绑定的方法替代。
4.3 拦截对象的特性访问
为了在访问特性的时候可以执行代码,必须使用一些魔法方法。
- __getattribute__(self, name):当特性被访问时自动调用。
- __getattr__(self, name):当特性name被访问且对象没有相应的特性时被自动调用。
- __setattr__(self, name, value):当试图给特性name赋值时会被自动调用。
- __delattr__(self, name):当时图删除特性name时自动调用。
5 迭代器
迭代器是带有next方法的简单对象。迭代器能在一系列的值上进行迭代。当没有值可供迭代时,next方法就会引发StopIteration异常。可迭代对象有一个返回迭代器的__iter__方法,它能像序列那样在for循环中使用。
下面是“斐波那契数列”的迭代器实现方法:
1 >>> class Fibs: 2 def __init__(self): 3 self.a = 0 4 self.b = 1 5 def next(self): 6 self.a, self.b = self.b, self.a+self.b 7 return self.a 8 def __iter__(self): 9 return self 10 11 >>> fib = Fibs() 12 >>> for f in fib: 13 if f >1000: 14 print f 15 break 16 返回结果:1597
内建函数iter可以从可迭代的对象中获得迭代器,使用list方法能够显示地将迭代器转化为列表。
1 >>> it = iter([1, 2, 3]) 2 >>> it.next() 3 1 4 >>> list(it) 5 [2, 3] 6 >>> it.next() 返回一个StopIteration异常 7 8 Traceback (most recent call last): 9 File "<pyshell#15>", line 1, in <module> 10 it.next() 11 StopIteration
6 生成器
生成器是一种用普通的函数语法定义的迭代器,它是包含了关键字yield的函数。当被调用时,在函数体中的代码不会被执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,知道遇到一个yield或者return语句。可以使用send、throw和close方法让活动生成器和外界交互。
生成器函数每次产生一个值,函数就会被冻结:即函数停在那点等待被激活。函数被激活后就从停止的那点开始执行。
1 >>> def flatten(nested): 2 for sublist in nested: 3 for element in sublist: 4 yield element 5 6 >>> nested = [[1],[2,3],[4,5,6]] 7 >>> for num in flatten(nested): 8 print num 9 结果: 10 1 11 2 12 3 13 4 14 5 15 6 16 >>> list(flatten(nested)) 17 结果:[1, 2, 3, 4, 5, 6]
生成器是由两部分组成:生成器的函数和生成器的迭代器。生成器的函数是用def语句定义的,包含yield的部分,生成器的迭代器是这个函数返回的对象。
6.1、生成器方法
外部作用域访问生成器用send方法,和访问next方法一样,send方法要使用一个参数。并且在生成器挂起时,yield要作为表达式而不是语句使用。
1 >>> def repeater(value): 2 while True: 3 new = (yield value) 4 if new is not None: 5 value = new 6 7 >>> r = repeater(22) 8 >>> r.next() 9 22 10 >>> r.send('hello') 11 'hello' 12 >>> r.next() 13 'hello'
注意:使用send方法只有在表达式挂起之后才有意义。
throw方法用于在生成器内引发一个异常(在yield表达式中)。
close方法用于停止生成器,建立在异常的基础之上,在需要的时候也会由Python垃圾收集器调用。
6.2 使用普通的函数模拟生成器
将生成器对象模拟成一个列表对象
1 def flatten(nested): 2 try: 3 # Don't iterate over string-like objects: 4 try: nested + '' 5 except TypeError: pass 6 else: raise TypeError 7 for sublist in nested: 8 for element in flatten(sublist): 9 yield element 10 except TypeError: 11 yield nested 12 13 def flatten(nested): 14 result = [] 15 try: 16 # Don't iterate over string-like objects: 17 try: nested + '' 18 except TypeError: pass 19 else: raise TypeError 20 for sublist in nested: 21 for element in flatten(sublist): 22 result.append(element) 23 except TypeError: 24 result.append(nested) 25 return result
这两个函数等价,一个返回生成器的迭代器对象,一个返回列表对象。