• 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

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

  • 相关阅读:
    大话数据结构笔记
    zsh安装教程
    Matlab安装教程
    7-16 插入排序还是归并排序 (25 分)
    7-14 插入排序还是堆排序 (25 分)
    7-14 二叉搜索树的最近公共祖先 (30 分)
    7-11 笛卡尔树 (25 分)
    中缀转换为后缀和前缀
    7-15 水果忍者 (30 分)
    兔子的区间密码(思维)
  • 原文地址:https://www.cnblogs.com/wanghaoran/p/2996998.html
Copyright © 2020-2023  润新知