1、生成器
生成器的创建方法:
(1)通过列表生成式创建
可以通过将列表生成式的[]改成()
eg: # 列表生成式 L = [ x*2 for x in range(5)] # L = [0, 2, 4, 6, 8] # 生成器 G = ( x*2 for x in range(5)) # 此时的G是,<generator object <genexpr> at 0x7f626c132db0>
创建列表生成式和生成器的区别只是最外层的()和[],列表生成式是一个列表,而生成器事宜个可迭代对象。生成器对象可以通过for语句遍历每个元素。
for each in G: print(each) # 打印结果是 0 2 4 6 8
(2)通过函数来实现
generator非常强大,如果推算的算法比较复杂,用类似的列表生成式的for循环无法实现的时候,可以通过函数来实现。
以著名的斐波拉契数列(Fibonacci), 除第一个和第二个数外,任意一个数都可以由前两个数相加得到:1,1,2,3,5,8,13….
斐波拉契数列用列表生成式不容易写出来,但是用函数把它打印出来很容易:
def fib(times): n = 0 a, b = 0, 1 while n < times: yield b # 这里用的yield将b的值返回到生成器中。 a, b = b, a+b n += 1 return 'done' f = fib(5) for each in f: print(each)
上述例子中,在循环过程中不断调用yield,就不会中断函数,但是必须要指定一个结束循环的条件,不然会产生一个无限循环的数列出来。
上述中,用for循环是拿不到return语句的返回值的,如果想拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
g = fib(5) while True: try: x = next(g) print('value: %d' % x) except StopIteration as e: print('生成器返回值:%s' % e.value) break
这样就可以取到return的返回值了。
(3)生成器的特点
生成器是这样一个函数,它记住上一次返回时在函数体中的位置。对生成器函数的第二次(或第 n 次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变。
生成器不仅“记住”了它数据状态;生成器还“记住”了它在流控制构造(在命令式编程中,这种构造不只是数据值)中的位置。
生成器的特点:
节约内存
迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的
2、迭代器
迭代是访问集合的一种方式。迭代器是一个可以记住遍历的位置的对象,迭代器对象从第一个元素开始访问,直到所有的元素被访问完结束,迭代器只能往前不能后退。
(1)可迭代对象
可迭代对象可以直接用户for循环的数据类型:
- 第一类是集合数据类型:如list,tuple,set,dict,str等等。
- 第二类是generator,包括生成器和代yield的生成器函数。
(2)判断是否为可迭代对象
可以使用isinstance()函数来判断一个对象是否是可迭代对象。
from collections import Iterable isinstance([], Iterable) # 如果是可迭代对象则返回True,反之返回False。
生成器不但可以作用于for循环,而且还可以被next()函数不断调用,知道抛出StopIteration错误,表示无法继续返回下一个值了。
(3)迭代器
可以被next()函数调用并不断返回下一个值得对象称为迭代器(Iterator)
可以使用isinstance()函数来判断一个对象是否是迭代器对象。
from collections import Iterator isinstance((x for x in range(10)), Iterator) # 如果是迭代器则返回True,反之False
(4)iter()函数
生成器都是Iterator对象,但是list,dict,str虽然是Iterable,但是不是Iterator。如果想把list,dict,str等可迭代对象变成迭代器,可以使用iter()函数。
isinstance 大专栏 python语法生成器、迭代器、闭包、装饰器总结(iter([]), Iterator) # 返回值是True,证明iter([])是迭代器。
(5)总结
- 凡是可以用for循环的对象,都是可迭代对象类型(iterable)
- 凡是可用于next()函数的对象,都是iterator(迭代器)类型。
- 集合数据类型,如list,dict,str等是可迭代对象,但不是迭代器,可以通过iter()函数获得一个迭代器对象。
3、闭包
闭包是在函数内部再定义一个函数,并且这个函数用到了外部函数的变量,就将这个函数以及用到的一些变量称之为闭包。简言之,内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包。
闭包示例:
def line_conf(a, b): def line(x): return a*x + b return line line1 = line_conf(1, 1) line2 = line_conf(4, 5) print(line1(5)) print(line2(5))
上例中,函数line与变量a, b构成闭包。在创建闭包的时候,我们通过line_conf的参数a, b说明了这两个变量的取值,这样,我们就确定了函数的最终形式(y = x + 1 和 y = 4x + 5)。我们只要变换参数a, b就可以得到不同的曲线表达函数,由此,闭包也具有提高代码可重复用性的作用。
如果没有闭包,我们需要每次创建直线函数的时候同时说明a, b, x。这样,我们需要更多的参数传递,也减少了代码的可移植性。
闭包总结:
- 闭包优化了变量,原来需要类对象完成的工作,闭包也可以完成。
- 由于闭包引用的外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存。
4、装饰器
装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代的函数。这个替代的函数可以增添新的功能。
(1)装饰器(decorator)功能
- 引入日志
- 函数执行时间统计
- 执行函数前的预处理
- 执行函数后的清理功能
- 权限校验等场景
- 缓存
(2)装饰器示例
1. 无参数函数
from time import sleep, ctime def timefun(func): def wrappedfunc(): print('%s is called at %s'%(func.__name__, ctime()) func() return wrappedfunc @timefunn def foo(): print('I am foo') foo() sleep(2) foo()
2. 被修饰的函数有参数
from time import ctime, sleep def timefunc(func): def wrappedfunc(a, b): print('%s is called at %s'%(func.__name, ctime()) print(a, b) func(a, b) return wrappedfunc @timefunc def foo(a, b) print(a+b) foo(3, 5) sloop(2) foo(2, 4)
3. 被修饰函数带有不定长参数
from time import ctime, sleep def timefunc(func): def wrappedfunc(*args, **kwargs): print('%s is called at %s'%(func.__name__, ctime()) func(*args, **kwargs) return wrappedfunc @timefunc def foo(a, b, c): print(a+b+c) foo(3,5,2) sleep(2) foo(2, 4, 9)
4.装饰器中带有return
from time import ctime, sleep def timefunc(func): def wrappedfunc(): print('%s called at %s'%(func.__name__, ctime()) func() return wrappedfunc @timefunc def foo(): print('i am foo') @timefunc def getInfo(): return '----hahahah-----' foo() sleep(2) foo() print(getInfo()) #执行结果是 foo called at .... I am foo foo called at ..... I am foo getInfo called at ... None
有结果可见,带有return的修饰器,如果还是按照上述的写法,则不会返回值。如果把修饰器函数中的func(),改为return func(),则print(getInfo())的执行结果是:
getInfo called at …
—–hahahah—–
这样return能正确返回。
来源:https://www.cnblogs.com/lijianming180/p/12286200.html