魔法方法、特性和迭代器

  • 构造函数 
在python中,创建构造函数和容易,只需将方法init的名称从普通的init改为魔法版__init__即可.
1
class FooBar: 2 def __init__(self): 3 self.somevar= 42 4 f = FooBar() 5 a = f.somevar 6 print(a)
执行结果
   
给构造函数添加几个参数
1
class FooBar: 2 def __init__(self, value = 42): 3 self.somevar= value 4 f = FooBar() 5 a = f.somevar 6 print(a)
执行结果
   
指定参数
1
class FooBar: 2 def __init__(self, value = 42): 3 self.somevar= value 4 f = FooBar('he he xiaoming') 5 a = f.somevar 6 print(a)
执行结果

 

  • 重写普通方法和特殊的构造函数
类A定义了一个名为hello的方法,并被类B继承.
1
class A: 2 def hello(self): 3 print("Hello, I'm A.") 4 class B(A): 5 pass 6 a = A() 7 b = B() 8 a.hello() 9 b.hello()
执行结果
 B重写方法hello
1
class A: 2 def hello(self): 3 print("Hello, I'm A.") 4 class B(A): 5 def hello(self): 6 print("Hello, I'm B.") 7 a = A() 8 b = B() 9 a.hello() 10 b.hello()
执行结果
    
 这个类定义了所有鸟都具备的一种基本能力:进食。从这个示例可知,鸟进食后就不在饥饿
1
class Bird: 2 def __init__(self): 3 self.hungry = True 4 def eat(self): 5 if self.hungry: 6 print('Aaaah...') 7 self.hungry = False 8 else: 9 print('NO, thanks!') 10 b = Bird() 11 b.eat() 12 b.eat()
执行结果
    
 子类SongBird,它新增了鸣叫功能 
1
class Bird: 2 def __init__(self): 3 self.hungry = True 4 def eat(self): 5 if self.hungry: 6 print('Aaaah...') 7 self.hungry = False 8 else: 9 print('NO, thanks!') 10 class SongBird(Bird): 11 def __init__(self): 12 self.sound = 'Squakw!' 13 def sing(self): 14 print(self.sound) 15 sb = SongBird() 16 sb.sing()
执行结果
   SongBird是Bird的子类,继承了方法eat,如果你尝试调用它,将发现一个问题.
1
class Bird: 2 def __init__(self): 3 self.hungry = True 4 def eat(self): 5 if self.hungry: 6 print('Aaaah...') 7 self.hungry = False 8 else: 9 print('NO, thanks!') 10 class SongBird(Bird): 11 def __init__(self): 12 self.sound = 'Squakw!' 13 def sing(self): 14 print(self.sound) 15 sb = SongBird() 16 sb.sing() 17 sb.eat()
执行结果
  异常清楚地指出了在什么地方:SongBird没有属性hungry,因为在SongBird中重写了构造函数,担心的构造函数没有包含任何初始化属性hungry的代码.
要消除这种错误,SongBird的构造函数必须调用其超类(Bird)的构造函数,以确保基本的初始化得以执行。为此,有两种方法:调用未关联的超类构造函
数,以及使用函数super。
    

 

  • 调用未关联的超类构造函数     
 在SongBird类中,只添加了一行,其中包含代码Bird.__init__(self).对实例用方法时,方法的参数self将自动关联到实例(称为关联的方法)。然而,
如果你通过类调用方法(如Bird.__init__),就没有实例与其相关联。在这种情况下,你可随便设置参数self.这样的方法称为未关联的
1
class Bird: 2 def __init__(self): 3 self.hungry = True 4 def eat(self): 5 if self.hungry: 6 print('Aaaah...') 7 self.hungry = False 8 else: 9 print('NO, thanks!') 10 class SongBird(Bird): 11 def __init__(self): 12 Bird.__init__(self) 13 self.sound = 'Squakw!' 14 def sing(self): 15 print(self.sound) 16 sb = SongBird() 17 sb.sing() 18 sb.eat()
执行结果
    

 

  • 使用函数super  
  如果你使用的不是旧版python,就应使用函数super.这个函数只是用于新式类。调用这个函数时,将当前类和当前实例作为参数。对其返回的对象调用方法
时,调用的将是超类(而不是当前类)的方法。因此,在songBird的构造函数中,可不适用Bird,而是shiyongsuper(SongBird,self)。
1
class Bird: 2 def __init__(self): 3 self.hungry = True 4 def eat(self): 5 if self.hungry: 6 print('Aaaah...') 7 self.hungry = False 8 else: 9 print('NO, thanks!') 10 class SongBird(Bird): 11 def __init__(self): 12 super().__init__() 13 self.sound = 'Squakw!' 14 def sing(self): 15 print(self.sound) 16 sb = SongBird() 17 sb.sing() 18 sb.eat() 19 sb.eat()
执行结果
    

 

  •  基本的序列和映射协议
  1. __len__(self): 这个方法应返回集合包含的项数,对序列来说为元素个数,对映射来说为键-值对数。如果__len__返回零(且没有实现覆盖这种行为的__nonzero__),对象在布尔上下文中将被视为假(就像空的列表、元组、字符串和字典一样)
  2. __getitem__(self, key):这个方法应返回与指定键相关联的值。对序列来说,键应该是0~n - 1的整数(也可以是负数),其中n为序列的长度。对映射来说,键可以使任何类型。
  3. __setitem__(self, key, value):这个方法应以与键相关联的方式存储值,以便以后能够使用__getitem__来获取。当然,仅当对象可变是才需要实现这个方法。
  4. __delitem__(self, key):这个方法在对对象的组成部分使用__del__语句时被调用,应删除与key相关联的值。同样,仅当对象可变(且允许其项被删除)时,才需要实现这个方法。

         对于这些方法,还有一些额外的要求

         1. 对于序列:如果键为负整数,应从末尾往前数。换而言之,x[-n]应与x[len(x)-n]等效。

         2.如果键的类型不合适(如对序列使用字符串键),可能引发TypeError异常。

         3.对于序列,如果索引的类型是正确的,但不在允许的范围内,应引发IndexError异常。

 

           

  这些代码实现的是一个算数序列,其中任何两个相邻数字的差都相同。第一个值时候构造函数的参数start(默认为0)指定的,而相邻之间的拆是由参数
step(默认为1)指定的。
1
def check_index(key): 2 if not isinstance(key, int): raise TypeError 3 if key < 0: raise IndexError 4 class ArithmeticSequence: 5 def __init__(self, start=0, step=1): 6 self.start = start 7 self.step = step 8 self.changed = {} 9 def __getitem__(self, key): 10 check_index(key) 11 try: return self.changed[key] 12 except KeyError: 13 return self.start + key * self.step 14 def __setitem__(self, key, value): 15 check_index(key) 16 self.changed[key] = value 17 s= ArithmeticSequence(1,2) 18 a = s[4] 19 print(a) 20 b = s[5] 21 print(b) 22 c = s[6] 23 print(c)
执行结果
    

 

  • 从list、dict和str派生    
 使用super来调用超类的相应方法,并添加了必要的行为:初始化属性counter(在__init__中)和更新属性counter(在__getitem__中)
>>> class CounterList(list): ... def __init_(self, *args): ... super().__init__(*args) ... self.counter = 0 ... def __getitem__(self, index): ... self.counter += 1 ... return super(CounterList, self).__getitem__(index) ... >>> cl = CounterList(range(10)) >>> cl [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> cl.reverse() >>> cl [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] >>> del cl[3:6] >>> cl [9, 8, 7, 3, 2, 1, 0]

 

  • 特性 
 get_size和set_size是假想属性size的存取方法,这个属性是一个由width和height组成的元组。通过存取方法定义的属性通常称为特性
1
class Rectangle: 2 def __init__(self): 3 self.width = 0 4 self.height = 0 5 def set_size(self, size): 6 self.width, self.height = size 7 def get_size(self): 8 return self.width, self.height 9 r = Rectangle() 10 r.width = 10 11 r.height = 5 12 a = r.get_size() 13 print(a) 14 r.set_size((150, 100)) 15 b = r.width 16 print(b)
执行结果
     

 

  • 函数property   
  在这个新版的Rectangle中,通过调用函数property并将存取方法作为参数(获取方法在前,设置方法在后)创建了一个特性,然后将名称size关联到这个
特性。这样,你就能以同样的方式对待width、height和size,而无需关心他们是如何实现的。
1
class Rectangle: 2 def __init__(self): 3 self.width = 0 4 self.height = 0 5 def set_size(self, size): 6 self.width, self.height = size 7 def get_size(self): 8 return self.width, self.height 9 size = property(get_size, set_size) 10 r = Rectangle() 11 r.width = 10 12 r.height = 5 13 a = r.size 14 print(a) 15 r.size = 150, 100 16 b = r.width 17 print(b)
执行结果

 

  • 静态方法和类方法        
像这样手工包装和替换方式有点繁琐
1
class MyClass: 2 3 def smeth(): 4 print('This is a static method') 5 smeth = staticmethod(smeth) 6 7 def cmeth(cls): 8 print('This is a class method of', cls) 9 cmeth = classmethod(cmeth)
 装饰器可用于包装任何可调用的对象,并且可用于方法和函数。定义这个方法后,无需实例化类
1
class MyClass: 2 @staticmethod 3 def smeth(): 4 print('This is a static method') 5 6 @classmethod 7 def cmeth(cls): 8 print('This is a lcass method of', cls) 9 MyClass.smeth() 10 MyClass.cmeth()
执行结果
    

 

  •  __getattr__、__setattr__等方法
  1.  __getattribute__(self, name):在属性被访问时自动调用(只适用于新式类)
  2. __getattr__(self, name):在属性被访问而对象没有这样的属性时自动调用  
  3. __setattr__(self, name, value):试图给属性赋值时自动调用
  4. __delattr__(self, name):试图删除属性时自动调用

   

          

 这里使用的是魔法方法:
1
class Rectangle: 2 def __init__ (self): 3 self.width = 0 4 self.height = 0 5 def __setattr__(self, name, value): 6 if name == 'size': 7 self.width, self.height = value 8 else: 9 self. __dic__[name] = value 10 def __getattr__(self, name): 11 if name == 'size': 12 return self.width, self.height 13 else: 14 raise AttributeError()
  1. 即便涉及的属性不是size,也将调用方法__setattr__.因此这个方法必须考虑如下两种情形:如果涉及的属性为size,就执行与以前一样的

操作。之所以使用它而不是执行常规属性赋值,是因为旨在避免再次调用__setattr__,进而导致无限循环.

2. 仅当没有找到指定的属性时,才会调用方法__getattr__.这意味着如果指定的名称不是size,这个方法将引发AttributeError异常。这个要让类能

够正确的支持hasattr和getattr登内置函数时很重要。如果指定的名称为size,就是用前一个实现中的表达式.

 

  • 迭代器

                迭代器协议        

   这个迭代器实现了方法__iter__,而这个方法返回迭代器本身。
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 fibs = Fibs() 11 for f in fibs: 12 if f > 10: 13 print(f) 14 break
执行结果

 

                从迭代器创建序列

    除了对迭代器和可迭代对象进行迭代之外,还可将他们转换为序列。
1
class TestIterator: 2 value = 0 3 def __next__(self): 4 self.value += 1 5 if self.value > 10: raise StopIteration 6 return self.value 7 def __iter__(self): 8 return self 9 ti = TestIterator() 10 a = list(ti) 11 print(a)
执行结果
   

 

  • 生成器

                创建生成器

它首先迭代所提供嵌套列表中的所有子列表,然后按顺序迭代每个字列表的元素
1
def flatten(nested): 2 for sublist in nested: 3 for element in sublist: 4 yield element 5 nested = [[1, 2], [3, 4], [5]] 6 for num in flatten(nested): 7 print(num) 8 b = list(flatten(nested)) 9 print(b)
执行结果
    

 

               递归式生成器        

如果要展开一个列表,需要遍历所有的子列表,并对它们调用flatten,然后使用另一个for循环生成展开后的字列表中的所有元素。
1
def flatten(nested): 2 try: 3 for sublist in nested: 4 for element in flatten(sublist): 5 yield element 6 except TypeError: 7 yield nested 8 a = list(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8])) 9 print(a)
执行结果
   

 

              生成器方法

  1.  外部世界:外部世界可访问生成器的方法send,这个方法类似于next,但接受一个参数(要发送的“消息”,可以时是任何对象)
  2.  生成器:在挂起的生成器内部,yield可能用作表达式而不是语句。换而言之,当生成器重新运行时,yield返回一个值——通过send从外部

    世界发送的值。如果使用的是next,yield将返回None.

         

  圆括号将yield表达式括起来了,如果要以某种方式使用返回值,就用圆括号将其括起来吧
1
def repeater(value): 2 while True: 3 new = (yield value) 4 if new is not None: value = new 5 r = repeater(42) 6 a = next(r) 7 print(a) 8 b = r.send("hello, world!") 9 print(b)
执行结果

方法throw:用于在生成器中(yield表达式处)引发异常,调用时可提供一个异常类型、一个可选值和一个taceback对象
方法close:用于停止生成器,调用时无需提供任何参数

 

  • 模拟生成器    
  这段代码的意思是,如果只剩下最后一个皇后没有放好,就勉励所有可能的位置,并返回哪些不会引发冲突的位置,参数number为皇后总数,而参数state是一个元组,包含已放好
的皇后的位置。例如,假设总共有4个皇后,而前3个皇后的位置分别为1、3和0,如图
1
def conflict(state, nextX): 2 nextY = len(state) 3 for i in range(nextY): 4 if abs(state[i] - nextX) in (0, nextY - i): 5 return True 6 return False 7
   8
 9 def queens(num, state):
10     if len(state) == num-1:
11         for pos in range(num):
12             if not conflict(state, pos):
13                 yield pos
14 
15 a = list(queens(4, (1, 3, 0)))
16 print(a)
执行结果
    

  

 

  • 递归条件

 

  
1
def conflict(state, nextX): 2 nextY = len(state) 3 for i in range(nextY): 4 if abs(state[i] - nextX) in (0, nextY - i): 5 return True 6 return False 7 8 9 def queens(num=8, state=()): 10 for pos in range(num): 11 if not conflict(state, pos): 12 if len(state) == num-1: 13 yield (pos,) 14 else: 15 for result in queens(num, state + (pos,)): 16 yield (pos,) + result 17 18 a = list(queens(3)) 19 print(a) 20 b = list(queens(4)) 21 print(b)
执行结果

 

  •  扫尾工作

            

 1 import random
 2 
 3 def conflict(state, nextX):
 4     nextY = len(state)
 5     for i in range(nextY):
 6         if abs(state[i] - nextX) in (0, nextY - i):
 7             return True
 8     return False
 9 
10 
11 def queens(num=8, state=()):
12     for pos in range(num):
13         if not conflict(state, pos):
14             if len(state) == num-1:
15                 yield (pos,)
16             else:
17                 for result in queens(num, state + (pos,)):
18                     yield (pos,) + result
19 
20 def prettyprint(solution):
21     def line(pos, length=len(solution)):
22         return'. ' * (pos) + 'X ' + '. ' * (length-pos-1)
23     for pos in solution:
24         print(line(pos))
25 prettyprint(random.choice(list(queens(8))))
执行结果
     

 

           魔法方法:python中有很多特殊方法,其名称以两个下划线开头和结尾。这些方法的功能各不相同,大都由python在特定情况下自动调用。例如__init__是在对象创建后调用的。

           构造函数:很多面向对象语言中都有构造函数,对于你自己编写的每个类,都可能需要为它实现一个构造函数。构造函数名为__init__,在对象创建后被自动调用。

           重写:类可重写其超类中定义的方法(以及其他任何属性),为此只需实现这些方法即可。要调用被重写的版本,可直接通过超类调用未关联版本(旧式类),也可使用函数super来调用(新式类)。

           序列和映射:要创建自定义的序列或映射,必须事先序列和映射协议指定的所有方法,其中包括__getitem__和__setitem__等魔法方法。通过从list(或UserList)和dict(或UserDict)派生,可减少很多工作量。

           迭代器:简单的说,迭代器是包含方法__next__的对象,可用于迭代一组值。没有更多的值可供迭代是,方法__next__应引发StopIteration异常。可迭代对象包含方法__iter__,它返回一个像序列一样可用于for循环中的迭代器。通常,迭代器也是可迭代的,即包含返回迭代器本身的方法__iter__.

           生成器:生成器的函数是包含关键字yield的函数,它在被调用时返回一个生成器,已一种特殊的迭代器。要与活动的生成器交互,可使用方法send、throw和close.

           

 

             

                

 

posted on 2018-04-08 11:52  一年级的小明同学  阅读(193)  评论(0)    收藏  举报

导航