魔法方法、特性和迭代器
- 构造函数
在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()
执行结果

- 基本的序列和映射协议
- __len__(self): 这个方法应返回集合包含的项数,对序列来说为元素个数,对映射来说为键-值对数。如果__len__返回零(且没有实现覆盖这种行为的__nonzero__),对象在布尔上下文中将被视为假(就像空的列表、元组、字符串和字典一样)
- __getitem__(self, key):这个方法应返回与指定键相关联的值。对序列来说,键应该是0~n - 1的整数(也可以是负数),其中n为序列的长度。对映射来说,键可以使任何类型。
- __setitem__(self, key, value):这个方法应以与键相关联的方式存储值,以便以后能够使用__getitem__来获取。当然,仅当对象可变是才需要实现这个方法。
- __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__等方法
- __getattribute__(self, name):在属性被访问时自动调用(只适用于新式类)
- __getattr__(self, name):在属性被访问而对象没有这样的属性时自动调用
- __setattr__(self, name, value):试图给属性赋值时自动调用
- __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()
- 即便涉及的属性不是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)
执行结果

生成器方法
- 外部世界:外部世界可访问生成器的方法send,这个方法类似于next,但接受一个参数(要发送的“消息”,可以时是任何对象)
- 生成器:在挂起的生成器内部,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是一个元组,包含已放好8
的皇后的位置。例如,假设总共有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
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.







浙公网安备 33010602011771号