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

这两个函数等价,一个返回生成器的迭代器对象,一个返回列表对象。

 

posted @ 2013-04-20 21:44  Hala  阅读(1494)  评论(0编辑  收藏  举报