Python基础12-迭代器、生成器和装饰器
迭代器
迭代是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合。
特点:
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
- 迭代器只能往前访问元素不会后退
- 便于循环比较大的数据集合,节省内存
- 迭代器有两个基本的方法:iter() 和 next(),通过iter()生成迭代器,通过next()访问下一个元素
字符串,列表或元组对象都可用于创建迭代器:
>>>list=[1,2,3,4] >>> it = iter(list) # 创建迭代器对象 >>> print(it) <list_iterator object at 0x0000000001102BA8> >>> print (next(it)) # 输出迭代器的下一个元素 1 >>> print (next(it)) 2 >>>
迭代器对象可以使用常规for语句进行遍历:
list=[1,2,3,4] it = iter(list) # 创建迭代器对象 for x in it: print (x, end=" ")
迭代器对象也可以使用 next() 进行遍历:
import sys # 引入 sys 模块 list=[1,2,3,4] it = iter(list) # 创建迭代器对象 while True: try: print (next(it)) except StopIteration: sys.exit()
生成器
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
使用生成器实现斐波那契数列:
import sys def fibonacci(n): # 生成器函数 - 斐波那契 a, b, counter = 0, 1, 0 while True: if (counter > n): return yield a a, b = b, a + b counter += 1 f = fibonacci(10) # f 是一个迭代器,由生成器返回生成 while True: try: print (next(f), end=" ") except StopIteration: sys.exit()
生成器方法send()
def MyGenerator(): value = (yield 1) print("value:{}".format(value)) value = (yield value) print("value:{}".format(value)) gen = MyGenerator() print(gen.__next__()) print(gen.send(2)) print(gen.send(3))
运行结果与解释
1 value:2 2 value:3 File "C:/Project/test.py", line 308, in <module> print(gen.send(3)) StopIteration 上面代码的运行过程如下。 当调用gen._next()_方法时,python首先会执行MyGenerator方法的yield 1语句。由于是一个yield语句,因此方法的执行过程被挂起,而next方法返回值为yield关键字后面表达式的值,即为1,
打印1。 当调用gen.send(2)方法时,python首先恢复MyGenerator方法的运行环境。同时,将表达式(yield 1)的返回值定义为send方法参数的值,即为2。这样,接下来value=(yield 1)这一赋值语句
会将value的值置为2,打印value:2。继续运行会遇到yield value语句。因此,MyGenerator方法再次被挂起。同时,send方法的返回值为yield关键字后面表达式的值,也即value的值,为2,打印2。 当调用send(3)方法时MyGenerator方法的运行环境。同时,将表达式(yield value)的返回值定义为send方法参数的值,即为3。这样,接下来value=(yield value)这一赋值语句会将value的值
置为3,打印value:3。继续运行,MyGenerator方法执行完毕,故而抛出StopIteration异常。
总的来说,send方法和next方法唯一的区别是在执行send方法会首先把上一次挂起的yield语句的返回值通过参数设定,从而实现与生成器方法的交互。但是需要注意,在一个生成器对象没有执行next方法之前,由于没有yield语句被挂起,所以执行send方法会报错。例如
gen = MyGenerator() print(gen.send(2))
上面代码的输出为
Traceback (most recent call last): File "test.py", line 16, in <module> print gen.send(2) TypeError: can't send non-None value to a just-started generator
但是,上面代码是可以运行的
gen = MyGenerator() print gen.send(None)
当send方法的参数为None时,它与next方法完全等价。但是注意,虽然上面的代码可以接受,但是不规范。所以,在调用send方法之前,还是先调用一次next方法为好。
装饰器
在不影响原有函数代码及执行方法的前提下,在函数前增加代码功能。
通过一下代码例子讲解:
def w1(func): def inner(): # 验证 return func() return inner @w1 def f1(): print 'f1'
当写完这段代码后(函数未被执行),python解释器就会从上到下解释代码,步骤如下:
- def w1(func): ==>将w1函数加载到内存
- @w1
没错,从表面上看解释器仅仅会解释这两句代码,因为函数在没有被调用之前其内部代码不会被执行。
从表面上看解释器着实会执行这两句,但是 @w1 这一句代码里却有大文章,@函数名 是python的一种语法糖。
如上例@w1内部会执行一下操作:
- 执行w1函数,并将 @w1 下面的 函数 作为w1函数的参数,即:@w1 等价于 w1(f1)
所以,内部就会去执行:
def inner:
#验证
return f1() # func是参数,此时 func 等于 f1
return inner # 返回的 inner,inner代表的是函数,非执行函数
其实就是将原来的 f1 函数塞进另外一个函数中 - 将执行完的 w1 函数返回值赋值给@w1下面的函数的函数名
w1函数的返回值是:
def inner:
#验证
return 原来f1() # 此处的 f1 表示原来的f1函数
然后,将此返回值再重新赋值给 f1,即:
新f1 = def inner:
#验证
return 原来f1()
所以,以后业务部门想要执行 f1 函数时,就会执行 新f1 函数,在 新f1 函数内部先执行验证,再执行原来的f1函数,然后将 原来f1 函数的返回值 返回给了业务调用者。
如此一来, 即执行了验证的功能,又执行了原来f1函数的内容,并将原f1函数返回值 返回给业务调用
可以装饰具有处理n个参数的函数的装饰器
def w1(func): def inner(*args,**kwargs): # 验证 return func(*args,**kwargs) return inner @w1 def f1(arg1,arg2,arg3): print 'f1'
在定义装饰器时,也可以带入参数
user,passwd = 'chen71','chen71' def auth_func(auth_type): #接收装饰器的参数auth_type def out_wrapper(func): def wrapper(*args,**kwargs): if auth_type == 'local': username = input('username:') password = input('password:') if user == username and passwd == password: print('Welcome...') return func(*args,**kwargs) else: print('Wrong username or password') elif auth_type == 'net': print('Sorry,net is not working...') return wrapper return out_wrapper def index(): print('Welcome to index page...') @auth_func(auth_type = 'local') #指定装饰器的参数 def home(): print('Welcome to home page...') @auth_func(auth_type = 'net') def bbs(): print('Welcome to bbs page...') index() home() bbs()
一个函数应用多个装饰器
def w1(func): def inner1(*args,**kwargs): print('w1_1') func(*args,**kwargs) print('w1_2') return inner1 def w2(func): def inner2(*args,**kwargs): print('w2_1') func(*args,**kwargs) print('w2_2') return inner2 @w1 @w2 def f1(name): print('f1') print('arg:{}'.format(name))
运行结果
w1_1 w2_1 f1 arg:hello w2_2 w1_2
调用过程:函数先应用w1,调用inner1打印出w1_1,inner1执行过程中调用w2的inner2,inner2执行过程中调用f1,inner2执行完毕之后,跳转回inner1继续往下执行打印w1_2
参考链接:http://www.cnblogs.com/wupeiqi/articles/4980620.html
参考链接:http://blog.csdn.net/xionghuixionghui/article/details/65442554