python语法生成器、迭代器、闭包、装饰器总结

三世轮回 提交于 2020-02-09 09:44:40

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循环的数据类型:

  1. 第一类是集合数据类型:如list,tuple,set,dict,str等等。
  2. 第二类是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)总结
  1. 凡是可以用for循环的对象,都是可迭代对象类型(iterable)
  2. 凡是可用于next()函数的对象,都是iterator(迭代器)类型。
  3. 集合数据类型,如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。这样,我们需要更多的参数传递,也减少了代码的可移植性。

闭包总结:
  1. 闭包优化了变量,原来需要类对象完成的工作,闭包也可以完成。
  2. 由于闭包引用的外部函数的局部变量,则外部函数的局部变量没有及时释放,消耗内存。

4、装饰器

装饰器其实就是一个闭包,把一个函数当做参数然后返回一个替代的函数。这个替代的函数可以增添新的功能。

(1)装饰器(decorator)功能
  1. 引入日志
  2. 函数执行时间统计
  3. 执行函数前的预处理
  4. 执行函数后的清理功能
  5. 权限校验等场景
  6. 缓存
(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能正确返回。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!