迭代是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
以直接作用于 for 循环的数据类型有以下几种:
这些可以直接作用于 for 循环的对象统称为可迭代对象: Iterable 。
可以使用 isinstance() 判断一个对象是否是 Iterable 对象:
from collections import Iterable isinstance([],Iterable) # True isinstance({},Iterable) # True isinstance(123,Iterable) # False isinstance((x for x in range(10)),Iterable) # True
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。
可以使用 isinstance() 判断一个对象是否是 Iterator 对象:
from collections import Iterator isinstance([],Iterator) False isinstance({},Iterator) False isinstance((x for x in range(10)),Iterator) # True
生成器都是迭代器。
虽然list 、 tuple 、 dict 、 set 、 str 等是可迭代对象,但他们不是迭代器。可以通过iter()函数把可迭代对象编程迭代器。
isinstance(iter([]),Iterator) # True isinstance(iter({}),Iterator) # True isinstance(iter("asdf"),Iterator) # True
我们可以通过列表生成式来创建一个列表,但是收到内存的限制,列表的容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
1|1修改列表推导式创建生成器的方法
最简单的方法是把列表生成式中的 [ ] 改成 ( ) 就好了。
a = [x for x in range(10)] print(a) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] b = (x for x in range(10)) print(b) # <generator object <genexpr> at 0x03387DB0>
如何遍历生成器
我们发现生成器不是能直接打印出来的,我们可以通过next()函数来获得生成器的下一个返回值。
生成器保存的是算法,每次调用 next(G) ,就计算出 G 的下一个元素的值,直到计算到最后一个元素,没有更多的元素时,抛出 StopIteration 的异常。
**使用next() 或者__next __():** print(next(b)) # 0 print(next(b)) # 1 print(next(b)) # 2 print(next(b)) # 3 print(next(b)) # 4 print(next(b)) # 5 print(b.__next__()) # 6 print(b.__next__()) # 7 print(b.__next__()) # 8 print(b.__next__()) # 9 print(b.__next__()) # Traceback (most recent call last): # File "<input>", line 2, in <module> # StopIteration
那么有什么简单的方法呢?因为生成器是可迭代对象,也可以使用for循环来遍历它,并且不需要关心 StopIteration 异常。
b = (x for x in range(10)) for x in b: print(x) # 0 # 1 # 2 # 3 #...
如果如果生成器推算的算法比较复杂,用类似列表生成式的 for 循环无法实现的时候,还可以用函数来实现。把你要返回的值前面加yield 即可。
使用函数实现上面代码:
def fn(): for x in range(3): yield x # 遍历函数实现的生成器 f = fn() print(next(f)) # 0 print(next(f)) # 1 print(next(f)) # 2 print(next(f)) # Traceback (most recent call last): # File "<input>", line 1, in <module> # StopIteration
使用生成器实现斐波拉契数列:
def fib(count): n = 0 a,b = 0,1 while n < count: yield b a,b = b,a+b n += 1 return "done" f = fib(5) print(next(f)) # 1 print(next(f)) # 1 print(next(f)) # 2 print(next(f)) # 3 print(next(f)) # 5 print(next(f)) # Traceback (most recent call last): # File "<input>", line 1, in <module> # StopIteration: done
yield执行流程
我们在循环过程中不断调用 yield ,就会不断中断。当然要给循环设置一个条件来退出循环,不然就会产生一个无限数列出来。同样的,把函数改成generator后,我们基本上从来不会用 next() 来获取下一个返回值,而是直接使用 for 循环来迭代:
for x in fib(5): print(x) # 1 # 1 # 2 # 3 # 5
但是用for循环调用generator时,发现拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
f = fib(5) while True: try: print(next(f)) except StopIteration as e: print("生成器返回值:%s"%e.value) break # 1 # 1 # 2 # 3 # 5 # 生成器返回值:done
1|3send方法
def gen(): i = 0 html" target="_blank">while i<3: temp = yield i print(temp) i+=1 g = gen() print(g.__next__()) # 0 print(g.send(None)) # None # 1 print(next(g)) # None # 2 print(g.send("哈哈") # 哈哈 # Traceback (most recent call last): # File "<input>", line 1, in <module> # StopIteration
上面代码可以看出next()、next ()、send(None)是等价的并没有什么区别。
在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包
def test(number): def test_in(number_in): print("test_in函数 的number_in=%s"%number_in) return number_in+number # 返回test_in函数的引用 return test_in ret = test(20) print(ret(100)) # 相当于直接调用test_in函数,并给它传值100 # test_in函数 的number_in=100 # 120 print(ret(200)) # test_in函数 的number_in=200 # 220
在数学中,一次函数:y=kx+b,在一条确定的直线中,它的k、b是不变的。求y时,根据确定的k、b、x来求出。
def line_conf(k, b): def line(x): return k*x + b return line line1 = line_conf(1, 1) line2 = line_conf(4, 5) print(line1(5)) print(line2(5)) # 6 # 25
如果没有闭包,我们需要每次创建直线函数的时候同时说明a,b,x。这样,我们就需要更多的参数传递,也减少了代码的可移植性。
装饰器就是对一个函数进行装饰,给这个函数增加额外的功能。
def logging(func): def wrap(): print("正在打印日志!") func() return wrap
@logging # 该装饰器为函数增加了打印日志的额外功能,并且之前函数内部代码不会改变。
def login(): print("张三正在登陆。") login() # 正在打印日志! # 张三正在登陆。 4|2两个装饰器 def makeBold(fn1): def wrapped(): print("----1----") return "<b>"+fn1()+"</b>" return wrapped def makeItalic(fn2): def wrapped(): print("----2----") return "<i>"+fn2()+"</i>" return wrapped @makeBold @makeItalic def f1(): print("----3----") return "hello world" ret = f1() # 此时f1并不是f1函数,它是makeBold装饰器返回的wrapped函数的引用。 print(ret) """ 输出结果: ----1---- ----2---- ----3---- <b><i>hello world</i></b> """
调用流程:
一般情况下装饰器内部函数的参数都是不定长参数,保证通用性,确保装饰任何函数时都不会出错。
def logging(func): def wrap(*args,**kwargs): print("正在打印日志!") func(*args,**kwargs) return wrap
@logging # 该装饰器为函数增加了打印日志的额外功能,并且之前函数内部代码不会改变。
def login(name,dic): print("%s正在登陆。"%name) print(dic) login("李四",{"sex":"男"}) # 正在打印日志! # 李四正在登陆。 # {'sex': '男'} 4|4类装饰器 class Test(object): def __init__(self, func): print("---初始化---") print("func name is %s"%func.__name__) self.__func = func def __call__(self): print("---装饰器中的功能---") self.__func() @Test def test(): print("----test---") test() """
输出结果:
---初始化---
func name is test
---装饰器中的功能---
----test---
"""
说明:
总结
以上所述是小编给大家介绍的python中的生成器、迭代器、闭包、装饰器,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对小牛知识库网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
本文向大家介绍python生成器与迭代器详解,包括了python生成器与迭代器详解的使用技巧和注意事项,需要的朋友参考一下 列表生成式: 例一: a = [i+1 for i in range(10)] print(a) 输出: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 例二: L = [1, 2, 3, 4, 5] print([i*i for i in L if i>3]
本文向大家介绍python迭代器与生成器详解,包括了python迭代器与生成器详解的使用技巧和注意事项,需要的朋友参考一下 例子 老规矩,先上一个代码: 这个东西输出可以脑补一下, 结果是[20,21,22,23], 而不是[10, 11, 12, 13]。 当时纠结了半天,一直没搞懂,后来齐老师稍微指点了一下, 突然想明白了--真够笨的,唉。。好了--正好趁机会稍微小结一下python里面的生成
本文向大家介绍实例详解Python装饰器与闭包,包括了实例详解Python装饰器与闭包的使用技巧和注意事项,需要的朋友参考一下 闭包是Python装饰器的基础。要理解闭包,先要了解Python中的变量作用域规则。 变量作用域规则 首先,在函数中是能访问全局变量的: 然后,在一个嵌套函数中,内层函数能够访问在外层函数中定义的局部变量: 闭包 上面的嵌套函数就是闭包。 闭包 是指延伸了作用域的函数,在
本文向大家介绍老生常谈Python之装饰器、迭代器和生成器,包括了老生常谈Python之装饰器、迭代器和生成器的使用技巧和注意事项,需要的朋友参考一下 在学习python的时候,三大“名器”对没有其他语言编程经验的人来说,应该算是一个小难点,本次博客就博主自己对装饰器、迭代器和生成器理解进行解释。 为什么要使用装饰器 什么是装饰器?“装饰”从字面意思来谁就是对特定的建筑物内按照一定的思路和风格进行
本文向大家介绍JavaScript中的迭代器和生成器详解,包括了JavaScript中的迭代器和生成器详解的使用技巧和注意事项,需要的朋友参考一下 处理集合里的每一项是一个非常普通的操作,JavaScript提供了许多方法来迭代一个集合,从简单的for和for each循环到 map(),filter() 和 array comprehensions(数组推导式)。在JavaScript 1.7中
本文向大家介绍python中的装饰器详解,包括了python中的装饰器详解的使用技巧和注意事项,需要的朋友参考一下 在了解装饰器的之前一定要先了解函数作为参数传递, 什么是函数内嵌,请参考我之前写的博客函数简介 因为在python里面,函数也是对象,也可以作为参数进行传递.python装饰器本质也是一种特殊函数,它接收的参数是函数对象,然后动态地函数参数添加额外的功能,而不用修改原有的函数对象.p