Python学习笔记8(迭代器、生成器、装饰器)

1.列表生成式

要想学习生成器和迭代器,首先得了解另外一个概念,列表生成式。

想要生成一个0~9的列表的时候,首先想到的就是range(0,10)

>>>a = range(0,10)
>>>print(a)

#3.0输出结果
range(0,10)
#2.0输出结果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

在3.0的版本呢当中range只是用来生成一个迭代器了,但是在2.0的版本里可以使用range来快速生成list。

但是想要生成一个[1*1,2*2,3*3......10*10],怎么做呢?可以使用循环

1 >>>L=[]
2 >>>for x in range(1,11):
3 >>>    L.append(x*x)
4 >>>print(L)
5 #输出结果
6 [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

列表生成式,可以一步解决上述问题。

1 >>>L = [x*x for x in range(1,11)]
2 >>>print(L)
3 #输出结果
4 [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

列表生成式后还可加入if判断条件,例如筛选出偶数的平方。

1 >>>L = [x*x for x in range(1,11) if x%2 ==0]
2 >>>print(L)
3 #输出结果
4 [4, 16, 36, 64, 100]

还可以嵌套两层循环,生成全排列。

1 >>>L = [x+y for x in ‘ABCD‘ for y in "xyz"]
2 >>>print(L)
3 #输出结果
4 [‘Ax‘, ‘Ay‘, ‘Az‘, ‘Bx‘, ‘By‘, ‘Bz‘, ‘Cx‘, ‘Cy‘, ‘Cz‘, ‘Dx‘, ‘Dy‘, ‘Dz‘]

这就是列表生成式。

2.生成器

通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

要创建一个generator,有两种方法。

第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:

1 >>>L1 = [x*x for x in range(10)]
2 >>>print(L1)
3 >>>L2 = (x*x for x in range(10))
4 >>>print(L2)
5 #输出结果
6 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
7 <generator object <genexpr> at 0x0000012B35912E60>

创建L1和L2唯一的区别就是一个是[ ]一个是( ),L1是一个列表,L2就是一个生成器generator。

我们可以直接打印出列表L1的每一个元素,因为已经生成并且创建出来了,但是如何打印L2generator的元素呢?

可以使用next()函数获取generator的下一个返回值:

L2 = (x*x for x in range(10))
print(next(L2))
print(next(L2))
print(next(L2))
print(next(L2))
print(next(L2))
print(next(L2))
print(next(L2))
print(next(L2))
print(next(L2))
print(next(L2))
print(next(L2))

#输出结果
0
1
4
9
16
25
36
49
64
81

此时生成器里的元素已经被取完,当再增加一个next()的时候,会抛出StopIteration的错误。

1 Traceback (most recent call last):
2   File "D:/workspace/test/day1/hello.py", line 23, in <module>
3     print(next(L2))
4 StopIteration

当然,我们也不会一直输入next()去获取它的每一个元素,而且结合for循环来迭代它,并且不需要关心StopIteration的错误。

 1 L2 = (x*x for x in range(10))
 2 for i in L2:
 3     print(i)
 4
 5 0
 6 1
 7 4
 8 9
 9 16
10 25
11 36
12 49
13 64
14 81

第二种方法,如果函数定义中包含yield关键字,那么这个函数就不是普通函数了,而是一个geneartor。

例如,普通的斐波那契数列函数:

1 def fib(max):
2     n, a, b = 0, 0, 1
3     while n < max:
4         print(b)
5         a, b = b, a + b
6         n = n + 1
7     return ‘done‘

调用并且执行

1 a = fib(5)
2 print(a)
3
4 1
5 1
6 2
7 3
8 5
9 done

当把print(b) 换做是 yield b时候,普通的函数也就变成了生成器。

def fib(max):
    n,a,b = 0,0,1
    while n < max:
        #print(b)
        yield  b
        a,b = b,a+b
        n += 1
    return ‘done‘

a = fib(5)
print(a)

#输出为
<generator object fib at 0x000001EC67462E60>

生成器和普通函数区别:

普通函数是顺序执行,遇到return语句或者最后一行函数语句就返回;

而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

而要访问函数中的每一个元素,依旧是使用for循环

1 for i in a:
2     print(i)
3
4 1
5 1
6 2
7 3
8 5

我们可以看到拿到了想要的前五个斐波那契数列,但是没有得到return返回值“done”,

如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIterationvalue中。

 1 b = fib(5)
 2 while True:
 3     try:
 4         x = next(b)
 5         print(x)
 6     except StopIteration as e:
 7         print("return的返回值:",e)
 8         break
 9
10 1
11 1
12 2
13 3
14 5
15 return的返回值: done

关于异常的捕获,之后肯定会单独写一篇来详细介绍。

3.迭代器

我们已经知道,可以直接作用于for循环的数据类型有以下几种:

一类是集合数据类型,如listtupledictsetstr等;

一类是generator,包括生成器和带yield的generator function。

这些可以直接作用于for循环的对象统称为可迭代对象:Iterable

而生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。

可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator

生成器都是Iterator对象,但listdictstr虽然是Iterable,却不是Iterator

listdictstrIterable变成Iterator可以使用iter()函数:

你可能会问,为什么listdictstr等数据类型不是Iterator

这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。

Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

小结

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如listdictstr等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

Python的for循环本质上就是通过不断调用next()函数实现的,例如:

 1 # 首先获得Iterator对象:
 2 it = iter([1, 2, 3, 4, 5])
 3 # 循环:
 4 while True:
 5     try:
 6         # 获得下一个值:
 7         x = next(it)
 8     except StopIteration:
 9         # 遇到StopIteration就退出循环
10         break

4.装饰器

定义:本质就是函数,(装饰其他的函数)就是为其他函数添加附加功能。

原则:

1.不能修改被装饰函数的源代码

2.不能修改被装饰函数的调用方式

实现装饰器知识储备:

1.函数即“变量”

只需要理解函数像变量一样先定义,才能引用。定义的时候也会生成一个内存地址,供函数的内容存放。

函数名存储的就是函数体的内存地址。

2.高阶函数

a.把一个函数名当作实参传给另一个函数(可以在不修改被装饰函数源代码的情况下为其添加功能)。

b.返回值中包含函数名(不修改被装饰函数的调用方式)。

3.嵌套函数

高阶函数+嵌套函数==》装饰器

----------------------------------------------------我是分割线----------------------------------------------------

高阶函数在装饰器中的作用一:

 1 import time
 2
 3 def bar():
 4     time.sleep(3)
 5     print("in the bar")
 6
 7 def test(func):
 8     start_time = time.time()
 9     func()
10     stop_time = time.time()
11     print("the func run time is %s"%(stop_time-start_time))
12
13 test(bar)

原函数bar,把原函数名作为参数传递给test函数(其实是将bar函数的内存地址传递给了test函数),并且在test函数里加上括号就表示执行。

至此实现了在不改变函数源代码的情况下增加了某些功能(例如例子中增加统计运行时间),但是改变了调用方式,因为原来是bar()调用,现在改成了test(bai)这么调用,不符合装饰器的规则。

别急,后面还有变数。

高阶函数在装饰器中的作用二:

 1 import time
 2
 3 def bar():
 4     time.sleep(3)
 5     print("in the bar")
 6
 7 def test(func):
 8     print(func)
 9     return func
10
11 bar = test(bar)
12 bar()

在装饰的函数return中返回函数名,并且重新赋值给bar,再加上括号进行调用。

这样就没有改变调用方式,依旧是bar(),并且执行了test函数里的print。

好像是达成了装饰器的原则,没有修改源代码,没有改变调用方式。

但是,仔细想一想,有什么地方不对呢。这样做相当于是不是只是在函数调用之前增加了一些然并卵的骚操作,和这样的效果类似呢?

1 def bar():
2     time.sleep(3)
3     print("in the bar")
4
5 def test(func):
6     print(func)
7
8 test(bar)
9 bar()

也就是说,改变的是在函数运行之前的一些东西,并不是函数本身的功能。有人估计会说,那就把函数放在test里运行,类似于下面的:

 1 import time
 2
 3 def bar():
 4     time.sleep(3)
 5     print("in the bar")
 6
 7 def test(func):
 8     start_time = time.time()
 9     func()
10     stop_time = time.time()
11     print("the func run time is %s"%(stop_time-start_time))
12     return func
13
14 bar = test(bar)
15 bar()

这样,问题其实更大了。不想改变函数的调用方式,是不是意味着在执行bar()语句的时候,函数才会运行。但是,实际是在bar = test(bar)已经执行了一遍bar()函数(此时是带有新增加功能的)。而在执行bar()函数的时候又执行了一遍(此时是没有新增加功能的)。

别急,接下来,就用到所需要的第三个知识储备:嵌套函数了。

嵌套函数在装饰器中的作用一:

嵌套函数简单的讲就是函数中还有函数(废话。。。)

那么就给我们提供了一个思路,既然想在test函数中放入被装饰的函数,又不想在运行test的时候就运行了原函数,那么,是不是可以把这个函数放在嵌套的函数里呢?例如:

 1 import time
 2
 3 def bar():
 4     time.sleep(3)
 5     print("in the bar")
 6
 7 def test(func):
 8     def timmer():
 9         start_time = time.time()
10         func()
11         stop_time = time.time()
12         print("the func run time is %s"%(stop_time-start_time))
13     return timmer
14
15 bar = test(bar)
16 bar()

在运行bar = test(bar)的时候,并没有运行timmer函数,对吧,因为test函数里并没有调用的指令呀,反而是返回了timmer函数的内存地址给了新命名的bar。

在运行bar()的时候,实际上是在运行timmer()!!!  这时候才运行了最原始的bar函数,并且新增加了一个统计运行时间的功能。

至此,装饰器所需要的知识储备已经的铺垫完了。可以达到不修改源代码,不修改调用方式,给原函数增加一些功能。

语法糖@

 1 import time
 2
 3 def test(func):
 4     def timmer():
 5         start_time = time.time()
 6         func()
 7         stop_time = time.time()
 8         print("the func run time is %s"%(stop_time-start_time))
 9     return timmer
10
11 @test
12 def bar():
13     time.sleep(3)
14     print("in the bar")
15
16 #bar = test(bar)
17 bar()

语法糖@test  ==> bar = test(bar)

相当于帮你省去了重新给函数赋值的这么一个过程,想给哪个函数加就在哪个函数头部加上@装饰函数名,没有加的当然按原功能调用啦

这就是一个简单的装饰器例子。

关于参数

 1 import time
 2
 3 def test(func):
 4     def timmer():
 5         start_time = time.time()
 6         func()
 7         stop_time = time.time()
 8         print("the func run time is %s"%(stop_time-start_time))
 9     return timmer
10
11 @test
12 def bar1():
13     time.sleep(3)
14     print("in the bar")
15
16 @test
17 def bar2(name):
18     time.sleep(3)
19     print("name:",name)
20
21 bar1()
22 bar2(‘zhang‘)

原函数bar1和bar2都被增加了装饰器,不同的地方是bar2有个参数,这时候就有问题了。运行结果如下:

in the bar
the func run time is 3.0000603199005127
Traceback (most recent call last):
  File "D:/workspace/test/day1/hello.py", line 22, in <module>
    bar2(‘zhang‘)
TypeError: timmer() takes 0 positional arguments but 1 was given

原因在于,运行的bar2(),相当于运行的timmer()。在timmer()定义的时候没有给形参啊,肯定会报错,要想不报错,就给增加上不定长参数:*args和**kwargs

 1 import time
 2
 3 def test(func):
 4     def timmer(*args,**kwargs):
 5         start_time = time.time()
 6         func(*args,**kwargs)
 7         stop_time = time.time()
 8         print("the func run time is %s"%(stop_time-start_time))
 9     return timmer
10
11 @test
12 def bar1():
13     time.sleep(3)
14     print("in the bar")
15
16 @test
17 def bar2(name):
18     time.sleep(3)
19     print("name:",name)
20
21 bar1()
22 bar2(‘zhang‘)

这样,无论原函数是几个参数,都能应对,这才是完整的装饰器。

简单提一嘴,如果装饰器想分情况装饰,在@test(传入参数),装饰器里会根据传入的参数分情况执行。但是具体怎么做呢?再最外面加一层嵌套,并且return第一层的函数名。

有兴趣的可以试一下。copy一个例子吧。

 1 from threading import Thread
 2 import time
 3
 4 class TimeoutException(Exception):
 5     pass
 6
 7 ThreadStop = Thread._Thread__stop#获取私有函数
 8
 9 def timelimited(timeout):
10     def decorator(function):
11         def decorator2(*args,**kwargs):
12             class TimeLimited(Thread):
13                 def __init__(self,_error= None,):
14                     Thread.__init__(self)
15                     self._error =  _error
16
17                 def run(self):
18                     try:
19                         self.result = function(*args,**kwargs)
20                     except Exception,e:
21                         self._error =e
22
23                 def _stop(self):
24                     if self.isAlive():
25                         ThreadStop(self)
26
27             t = TimeLimited()
28             t.start()
29             t.join(timeout)
30
31             if isinstance(t._error,TimeoutException):
32                 t._stop()
33                 raise TimeoutException(‘timeout for %s‘ % (repr(function)))
34
35             if t.isAlive():
36                 t._stop()
37                 raise TimeoutException(‘timeout for %s‘ % (repr(function)))
38
39             if t._error is None:
40                 return t.result
41
42         return decorator2
43     return decorator
44
45 @timelimited(2)
46 def fn_1(secs):
47     time.sleep(secs)
48     return ‘Finished‘
49
50 if __name__ == "__main__":
51     print fn_1(4)

时间: 2024-08-07 17:02:44

Python学习笔记8(迭代器、生成器、装饰器)的相关文章

python学习笔记(5)--迭代器,生成器,装饰器,常用模块,序列化

生成器 在Python中,一边循环一边计算的机制,称为生成器:generator. 如: 1 >>> g = (x * x for xin range(10)) 2 >>> g3 <generator object <genexpr> at 0x1022ef630> 此处g就是一个生成器. 迭代器 我们已经知道,可以直接作用于for循环的数据类型有以下几种: 一类是集合数据类型,如list.tuple.dict.set.str等: 一类是gene

Python学习笔记__4.4章 装饰器(添加额外功能)

# 这是学习廖雪峰老师python教程的学习笔记 1.概览 装饰器可以帮助我们为已经存在的对象添加额外的功能 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象. 装饰器经常用于有切面需求的场景,比如:插入日志.性能测试.事物处理.缓存.权限校验等场景. 1.1.为now函数 加一行日志 # 定义now函数 def now(): print('2018-5-8') # 编辑decorator def log(func):

python学习笔记-(八)装饰器、生成器&amp;迭代器

本节课程内容概览: 1.装饰器 2.列表生成式&迭代器&生成器 3.json&pickle数据序列化 1. 装饰器 1.1 定义: 本质上是个函数,功能是装饰其他函数—就是为其他函数添加附加功能 1.2 装饰器原则: 1)  不能修改被装饰函数的源代码: 2)  不能修改被装饰函数的调用方式: 1.3 实现装饰器知识储备: 1.3.1 函数即“变量” 定义一个函数相当于把函数体赋值给了函数名 变量可以指向函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Python学习笔记(十一)装饰器

摘抄:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318435599930270c0381a3b44db991cd6d858064ac0000#0 本文章完全用来个人复习学习,侵删: 由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数. >>> def now(): ... print('2015-3-25') ... >

Python学习笔记(2)装饰器

1,.概念 装饰器英文名Decorator,装饰器的作用就是为已经存在的对象添加额外的功能.总的来说,装饰器其实就是一个函数,一个用来包装函数的函数,所以他的参数是被修饰的函数对象,返回一个修改之后的函数对象,将其重新赋值原来的标示符,并永久丧失对原始函数对象的访问. 2.装饰器语法 1.def deco(func): 2. print func 3. return func 4[email protected] 5.def foo():pass 6.foo() 第一个函数deco是装饰函数,它

Python学习笔记(yield与装饰器)

yeild:返回一个生成器对象: 装饰器:本身是一个函数,函数目的装饰其他函数(调用其他函数) 功能:增强被装饰函数的功能 装饰器一般接受一个函数对象作为参数,以便对其增强 @原函数名  来调用其他函数 递归:递归需要边界条件,递归前进段和递归返回段: 10*9*8*7*6*5*4*3*2*1 10*9 10*(10-1)*(10-1)-1) 协程: 函数的设计规范: 耦合性: (1)通过参数接受输入,以及通过return产生输出以保证函数的独立性: (2)尽量减少使用全局变量进行函数间通信:

Python学习笔记(12)装饰器

import time def show_time(f): def inner(a, b): start = time.time() f(a, b) end = time.time() print('spend %s' % (end - start)) return inner @show_time # add = show_time(add)def add(a, b): print(a + b) time.sleep(1) add(3, 4) import timeimport functoo

Python 迭代器&amp;生成器,装饰器,递归,算法基础:二分查找、二维数组转换,正则表达式,作业:计算器开发

本节大纲 迭代器&生成器 装饰器  基本装饰器 多参数装饰器 递归 算法基础:二分查找.二维数组转换 正则表达式 常用模块学习 作业:计算器开发 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式,运算后得出结果,结果必须与真实的计算器所得出的结果一致 迭代器&

python 迭代器 生成器 装饰器

迭代器 可以直接作用于for循环的对象统称为可迭代对象(Iterable). 可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator). 所有的Iterable均可以通过内置函数iter()来转变为Iterator. names = iter(['sun', 'ibm', 'sunny']) print(names) print(names.__next__()) print(names.__next__()) print(names.__next__()) print(

Python 全栈开发五 迭代器 生成器 装饰器

一.迭代器 迭代器是一个可以记住遍历的位置的对象.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退. 迭代器有两个基本的方法:iter() 和 next(). 字符串,列表或元组对象都可用于创建迭代器,生成迭代器的方法是iter(): >>li = [1,2,3,4,5] >>it = iter(li) #生成一个迭代器 >>it.__next__() 1 >>next(it) #以上两种next都可以使用 2 >