我们见过列表推导式,现在又了解到了字典推导式,它们的格式都是**[x for x in xx]** 或 {x for x in xx} ,但是,不要以为(x for x in xx)就是元组推导式了,其实这是一个生成器, 下面会讲!!
使用%进行字符串格式化的话,格式为 “xxx %d xxx %s…” % (arg1, arg2…),但是,如果我的arg就是元组的形式,且只有一个,如下例:
name = (1, 2, 3) # s2 = "My name is %s" % name # 会抛出异常 not all arguments converted during string formatting # 为了保证它总是正确的,你必须这样做: s2 = "My name is %s" % (name, ) print(s2) # 使用format就不会出现这样的情况: s3 = "My name is {}".format(name) print(s3) My name is (1, 2, 3) My name is (1, 2, 3)迭代 是访问元素的一种方式,迭代器 则是一个可以记住遍历位置的对象,它从集合的第一个元素开始访问,直到所有元素都访问完结束。
1、可迭代对象:简单来说,可以通过for … in … 这类语句迭代读取一条数据供我们使用的对象,称为可迭代对象(iterable)。本质上:可迭代对象可以向我们提供一个中间人,即迭代器,帮助我们进行迭代遍历使用。可迭代对象通过__iter__方法为我们提供一个迭代器,当我们遍历一个可迭代对象时,实际上就是先获取这个对象提供的一个迭代器,然后通过这个迭代器一次访问对象中的每一个元素。也就是说:一个具备了__iter__方法的对象,就是一个可迭代对象。 from collections import Iterable print("判断一个对象是不是可迭代对象:") print("[]是不是可迭代对象? ", isinstance([], Iterable)) print("'abcd'是不是可迭代对象? ", isinstance("abcd", Iterable)) print("100是不是可迭代对象? ", isinstance(100, Iterable)) 判断一个对象是不是可迭代对象: []是不是可迭代对象? True 'abcd'是不是可迭代对象? True 100是不是可迭代对象? False class A(): def __iter__(self): # 一个对象实现了__iter__方法,即是可迭代对象 pass a = A() print("a是不是可迭代对象? ", isinstance(a, Iterable)) a是不是可迭代对象? Trueiter()和next()函数: 像list、tuple等可迭代对象,均可以通过iter()获取它们的迭代器,然后对获取到的迭代器不断的next()获取下一条数据。注:iter()函数实际上就是调用了可迭代对象的__iter__方法。
a = [1, 2, 3] iter_ = iter(a) print(iter_) # list的迭代器 print(next(iter_)) # 对迭代器进行next获取下一条数据 print(next(iter_)) print(next(iter_)) print(next(iter_)) # 当迭代完最后一个数据之后,再次调用next()函数时会抛出一个StopIteration的异常提醒我们所有数据迭代完了。 <list_iterator object at 0x00000235FE404CC0> 1 2 3 --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-6-cf53863bae90> in <module>() 5 print(next(iter_)) 6 print(next(iter_)) ----> 7 print(next(iter_)) # 当迭代完最后一个数据之后,再次调用next()函数时会抛出一个StopIteration的异常提醒我们所有数据迭代完了。 StopIteration: # 判断一个对象是不是一个迭代器 from collections import Iterator print("[]是迭代器吗? ", isinstance([], Iterator)) print("iter([])是迭代器吗? ", isinstance(iter([]), Iterator)) []是迭代器吗? False iter([])是迭代器吗? True通过上面的分析,我们知道:迭代器是用来帮助我们记录每一次迭代访问到的位置,当我们使用next()方法时,迭代器会向我们返回它所记录位置的下一个位置。 实际上,我们使用next()方法时,就是调用迭代器对象的__next__方法,所以我们想要构造一个迭代器,就要实现它的__next__方法。python要求迭代器本身也是可迭代的,所以我们为迭代器实现__iter__方法,而__iter__方法要返回一个迭代器,迭代器自身是一个迭代器,所以返回自身就可以了。总结:一个实现了__iter__方法和__next__方法的对象,就是迭代器。
from collections import Iterable, Iterator class Mylist(): def __iter__(self): return self def __next__(self): pass mylist = Mylist() print("mylist是可迭代对象吗? ",isinstance(mylist, Iterable)) print("mylist是迭代器吗? ",isinstance(mylist, Iterator)) mylist是可迭代对象吗? True mylist是迭代器吗? True class Mylist(): """自定义一个可迭代对象""" def __init__(self): self.items = [] def add(self, val): self.items.append(val) def __iter__(self): # 实现__iter__方法,返回一个迭代器 mylist_iterator = Mylist_iterator(self) # 创建一个迭代器对象,传入的参数是可迭代对象的实例对象self print("我在Mylist()的__iter__(self)方法这......") return mylist_iterator # 返回这个迭代器 class Mylist_iterator(): """自定义一个供上面可迭代对象使用的迭代器""" def __init__(self, mylist): self.mylist = mylist self.cur = 0 # 记录当前位置 def __next__(self): print("我在Mylist_iterator()的__next__(self)方法这......") if self.cur < len(self.mylist.items): item = self.mylist.items[self.cur] self.cur += 1 return item else: raise StopIteration def __iter__(self): # 返回迭代器本身 return self mylist = Mylist() mylist.add(0) mylist.add(1) mylist.add(2) mylist.add(3) mylist.add(4) for i in mylist: print(i) 我在Mylist()的__iter__(self)方法这...... 我在Mylist_iterator()的__next__(self)方法这...... 0 我在Mylist_iterator()的__next__(self)方法这...... 1 我在Mylist_iterator()的__next__(self)方法这...... 2 我在Mylist_iterator()的__next__(self)方法这...... 3 我在Mylist_iterator()的__next__(self)方法这...... 4 我在Mylist_iterator()的__next__(self)方法这......for item in xx_obj:运行的本质:
1、先判断xx_obj是不是一个可迭代对象,即判断这个对象里面有没有__iter__方法2、在上面条件成立的情况下,调用iter()方法,得到xx_obj的__iter__方法的返回值,即一个迭代器3、迭代器不断调用next()方法,即迭代器里的__next__方法,获取下一个值给item,直到抛出StopIteration异常,结束。上面的方法可以集成为一个
class Mylist(): """自定义一个可迭代对象,也是一个迭代器""" def __init__(self): self.items = [] self.cur = 0 def add(self, val): self.items.append(val) def __iter__(self): return self def __next__(self): if self.cur < len(self.items): item = self.items[self.cur] self.cur += 1 return item else: raise StopIteration mylist = Mylist() mylist.add(0) mylist.add(1) mylist.add(2) mylist.add(3) mylist.add(4) for i in mylist: print(i, end=",") 0,1,2,3,4,迭代器的应用: 实现一个斐波那契数列:1, 1, 2, 3, 5, 8, …
class Fib(): def __init__(self, n): self.n = n self.a = 0 self.b = 1 self.cur = 0 def __iter__(self): return self def __next__(self): if self.cur < self.n: self.a, self.b = self.b, self.a + self.b self.cur += 1 return self.a else: raise StopIteration for i in Fib(5): print(i, end=" ") 1 1 2 3 5此外:并不是只有for循环可以接受可迭代对象,list、tuple等也能接受可迭代对象
print(list(Fib(5)), tuple(Fib(5))) [1, 1, 2, 3, 5] (1, 1, 2, 3, 5)利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时,按照特定的规律进行生成,但是我们需要自己手动记录当前的状态,如上例的self.cur,然后根据当前状态生成下一个数据。
为了达到记录当前状态并配合next()函数进行迭代使用,可以采用一种更加简洁的方式:生成器。生成器是一种特殊的迭代器。
1、创建生成器的方法(一)
L = [i for i in range(5)] # 列表推导式 print(L) G = (i for i in range(5)) # 注意,不是元组推导式,这是生成器 print(G) [0, 1, 2, 3, 4] <generator object <genexpr> at 0x00000235FE47FF68>使用next()获取下一个值,或者使用for循环、list等方法遍历。
G = (i for i in range(5)) print(next(G)) 0 G = (i for i in range(5)) print(list(G)) [0, 1, 2, 3, 4] G = (i for i in range(5)) for i in G: print(i, end=" ") 0 1 2 3 42、创建生成器的方法(二):yield
简单的说:一个函数中定义了yield关键字,定义的函数不在是函数,而是一个生成器。
def f(): yield print(f()) <generator object f at 0x00000235FE3E39E8> # 使用yield实现斐波那契数列 def fib(n): a = 0 b = 1 cur = 0 while cur < n: a, b = b, a + b cur += 1 yield a return "ok" F = fib(5) print(next(F)) print(next(F)) print(next(F)) print(next(F)) print(next(F)) print(next(F)) # 当迭代完最后一个数据之后,再次调用next()函数时会抛出一个StopIteration的异常提醒我们所有数据迭代完了。 1 1 2 3 5 --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) <ipython-input-18-1cee030fd9a7> in <module>() 17 print(next(F)) 18 print(next(F)) ---> 19 print(next(F)) # 当迭代完最后一个数据之后,再次调用next()函数时会抛出一个StopIteration的异常提醒我们所有数据迭代完了。 StopIteration: ok发现:return值拿不到,这个值在StopIteration异常里
def fib(n): a = 0 b = 1 cur = 0 while cur < n: a, b = b, a + b cur += 1 yield a return "ok" F = fib(5) try: while True: print(next(F)) except StopIteration as e: print("return值:", e.value) 1 1 2 3 5 return值: ok小结:
1、使用yield关键字的函数不再是函数,而是生成器2、yield关键字的作用: 保存当前运行状态(断点,有点像单片机里的中断),然后挂起生成器将yield关键字后面的表达式的值作为返回值返回,作用相当于return 3、当再次唤醒生成器时,会从断点处继续执行4、python3中可以使用return返回最终的运行返回结果,但是这个值是保存在StopIteration异常里,而python2中不允许使用return返回一个返回值,最允许使用return从生成器中退出,不能加任何表达式。除了使用next()唤醒生成器之外,还有一个send()方法,此外使用send的另一个优点是可以在唤醒的同时附带一个数据
def generate(): i = 0 while i < 5: xx = yield i print(xx) i += 1 g = generate() next(g) # 第一次,唤醒生成器,执行到xx = yield i,返回一个i(i=0)后退出,等待下一次唤醒 next(g) # 第二次唤醒,从上一个断点处继续往下执行,然后再次执行xx = yield i,返回,这里使用next()唤醒,xx没有接收参数,默认为None next(g) None None 2 def generate(): i = 0 while i < 5: xx = yield i print(xx) i += 1 g = generate() g.send(None) # <==> next(g) 第一次启动 g.send("lalala") next(g) # <==>g.__next__()不常用 lalala None 2AOP:在软件系统的实现过程中,在很多模块操作中都会用到一些相同的固定的逻辑操作,如:用户验证,面向切面编程就是将公共的逻辑和系统本身核心业务逻辑分离开,集中管理。一方面减轻系统本身的业务逻辑,另一方面降低了耦合,提高了可重用性,便于后期扩展维护。
在python中,装饰器常用于有切面需求的场景,装饰器可以在不改变源码的前提下,增加额外的业务功能,可以抽离出大量函数中与函数功能本身无关的雷同代码,并继续重用。具体细节可参考我的博客。