Python修炼之路-装饰器、生成器、迭代器
装饰器
本质:是函数,用来装饰其他函数,也就是为其他函数添加附加功能。
使用情景
1、不能修改被装饰的函数的源代码;
2、不能修改被装饰的函数的调用方式。
在这两种条件下,为函数添加附加功能,就可以使用装饰器。
装饰器的实现
装饰器的实现:高阶函数+嵌套函数 =》装饰器
1.函数即“变量”(匿名函数除外,没有函数名) def定义一个函数f,相当于变量 f 指向函数f的函数体(函数体保存在内存中)
2.高阶函数
把一个函数名当做实参传给另外一个函数(在不修改被装饰函数源代码的情况下为期添加新功能);
返回值包含函数名(不修改函数的调用方式)
3.嵌套函数
在一个函数的函数体内,用def去申明一个函数,区别函数调用
高阶函数+嵌套函数 =》装饰器
#函数即变量,函数名可以当做变量传入函数
import time
def bar():
time.sleep(3)
print('in the bar') def test1(func):
start_time=time.time()
func() #run bar
stop_time=time.time()
print("the func run time is %s" %(stop_time-start_time)) test1(bar)
bar()
#in the bar
#the func run time is 3.0
#in the bar #返回函数名
import time
def bar():
time.sleep(3)
print('in the bar')
def test2(func):
print(func)
return func bar=test2(bar)
bar()
# <function bar at 0x00000278B5FC8F28>
# in the bar
函数嵌套:区别函数调用
def foo():
print('in the foo')
def bar():
print('in the bar') bar()
foo()
#in the foo
#in the bar
使用装饰器
装饰器:被修饰的函数可以接受不确定数量的参数(*args,**kwargs)
装饰器接受一个函数作为参数,并返回一个函数的高阶函数,采用python的@语法。
把@timer放在test1上面个相当于执行test1=timer(test1),变量test1指向新的函数deco,可以使用函数对象属性__name__查看。
import time
def timer(func): #timer(test1) func=test1
def deco(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs) #run test1()
stop_time = time.time()
print("the func run time is %s" %(stop_time-start_time))
return deco @timer #test1=timer(test1)
def test1():
time.sleep(1)
print('in the test1') @timer # test2 = timer(test2) = deco test2(name) =deco(name)
def test2(name,age):
print("test2:",name,age) test1()
test2("a",22) # in the test1
# the func run time is 1.0000903606414795
# test2: a 22
# the func run time is 0.0
装饰器:装饰器函数可以接受参数。
@log()放置now()函数的定义处,相当于执行语句:now = log()(now),变量now指向新的函数即:wrapper, 可以调用函数对象的属性__name__查看。
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@log('excute')
def now():
print('2015-3-25') now()
# excute now():
# 2015-3-25
functools.wraps定义函数装饰器
有无functools.wraps区别:使用help(func)返回值不同;若无修饰,则返回wrapper(*args, **kw),函数的名字变成装饰器中的包装器了,help内置函数也失效了;否则返回func()。即若要保留函数原来属性,就可以用functools.wraps
import functools
def log(logorfunc='default_call'):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('begin %s %s():' % (acstr, func.__name__))
ret = func(*args, **kw)
print('end %s %s():' % (acstr, func.__name__))
return wrapper
if isinstance(logorfunc, str):
acstr = logorfunc
return decorator
else:
acstr = 'void_call'
return decorator(logorfunc)
@log('execute')
def now():
print('2017-1-11')
@log()
def now2():
print('2017-1-12')
@log
def now3():
print('2017-1-13')
for f in (now, now2, now3):
f()
print("f's name is: %s\n" % f.__name__)
"""
begin execute now():
2017-1-11
end execute now():
f's name is: now begin default_call now2():
2017-1-12
end default_call now2():
f's name is: now2 begin void_call now3():
2017-1-13
end void_call now3():
f's name is: now3
"""
实例
登入认证控制:
import time
user,passwd = 'user','abc123'
def auth(auth_type):
print("auth func:",auth_type)
def outer_wrapper(func):
def wrapper(*args, **kwargs):
print("wrapper func args:", *args, **kwargs)
if auth_type == "local":
username = input("Username:").strip()
password = input("Password:").strip()
if user == username and passwd == password:
print("\033[32;1mUser has passed authentication\033[0m")
res = func(*args, **kwargs) # from home
print("---after authenticaion ")
return res
else:
exit("\033[31;1mInvalid username or password\033[0m")
elif auth_type == "ldap":
print("no this service") return wrapper
return outer_wrapper def index():
print("welcome to index page")
@auth(auth_type="local") # home = wrapper()
def home():
print("welcome to home page")
return "from home" @auth(auth_type="ldap")
def bbs():
print("welcome to bbs page") index()
print(home()) #wrapper()
bbs() # auth func: local
# auth func: ldap
# welcome to index page
# wrapper func args:
# Username:user
# Password:abc123
# User has passed authentication
# welcome to home page
# ---after authenticaion
# from home
# wrapper func args:
# no this service
生成器
如果一个列表包含100万个元素,那么需要很大空间存储;由于内存限制,列表的容量是有限的。如果对这个列表仅仅访问前面几个元素,那么后面的绝大部分元素占用的空间就白白浪费。
在python中,根据某种算法推算出列表的下一个元素,不用创建完整的list,从容节省大量内存空间。这种一边循环一边计算的机制称为生成器。
和列表的区别:
创建区别:列表使用[],生成器()
使用:可以直接打印列表的每一个元素,生成器只能一个一个的向后取
生成器调用:生成器只有一个方法,g.__next__() (2.7中为next()) 调用一次,返回一个元素(只能向后获取元素,不能回到上一个元素),通过next()函数来获取generator返回值。
生成器只有在调用时才会生成相应的数据;生成器只记录当前元素(只保留一个值),只能向后一个一个的获取
生成器一般通过for循环来迭代,对于复杂的生成器可以通过函数来实现。
列表生成式
列表生成式:创建一个列表,简化代码
[ i for i in range(5)] #[0,1,2,3,4]
[ func(i) for i in range(5)]
实例
#与if语句配合使用 [x * x for x in range(1, 11) if x % 2 == 0]
#[4, 16, 36, 64, 100] #嵌套for语句
[m + n for m in 'ABC' for n in 'XYZ']
#['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ'] d = {'x': 'A', 'y': 'B', 'z': 'C' }
[k + '=' + v for k, v in d.items()]
#['y=B', 'x=A', 'z=C']
生成器
生成器generator:使用()定义生成器,它将计算g下一个元素,直到最后一个调用时。如果没有更多的元素,它将抛出stopIteration出现错误。
g = (x * x for x in range(10))
for n in g:
print(n)
yield语句
对于复杂的生成器,可以使用函数来实现,使用yield语句把函数改为生成器。
函数与生成器的区别:
函数是顺序执行,遇到return
语句或者最后一行函数语句就返回;
变成generator的函数,在每次调用next()
的时候执行,遇到yield
语句就中断并返回,再次执行时从上次返回的yield
语句处继续执行。
使用generator不断获取下一个元素时,需要设置一个退出循环的条件,也可以用try...except来捕获异常退出,并且可以获取返回值,返回值包含在StopIteration的 value中。使用for循环调用generator时,获取不到generator的return语句的返回值。
#斐波那契数列:
#函数:定义了斐波那契数列的规则
def fib(max): #
n, a, b = 0, 0, 1
while n < max: #n<10
print(b)
a, b = b, a + b
n = n + 1
return '---done---' fib(6) #1 1 2 3 5 8 #生成器 print(b) 换成yeild b
def fib(max): #
n, a, b = 0, 0, 1
while n < max: #n<10
yield b
a, b = b, a + b
n = n + 1
return '---done---' g = fib(6)
while True:
try:
x = next(g)
print('g:', x)
except StopIteration as e:
print('Generator return value:', e.value)
break
"""
g: 1
g: 1
g: 2
g: 3
g: 5
g: 8
Generator return value: ---done--- """
生产者消费者模型
把函数变成生成器后,可以使函数停在某个位置,下次调用时,从上次停止的地方开始继续运行。
作用:实现程序在单线程中的并行运行效果(协程)
#生产者消费者模型 异步IO的雏形
import random def role_red():
print("Create role successful... ")
red = 100
while True:
red1 = yield red
red -= red1 player1 = role_red()
player2 = role_red()
next(player1)
next(player2)
n = 1
while True:
print("第%s回合" % n)
i = random.randint(30,55)
j = random.randint(25,50)
player1_red = player1.send(j)
player2_red = player2.send(i)
print("player1: %s" % player1_red)
print("player2: %s" % player2_red)
if player1_red <= 0:
print("player1 is failed." )
break
if player2_red <= 0:
print("player2 is failed")
break
n += 1 """
Create role successful...
Create role successful...
第1回合
player1: 74
player2: 46
第2回合
player1: 37
player2: -7
player2 is failed
"""
理解yield与send方法:
yield 返回空;yield value 返回value; receive = yield value,返回value,receive赋值为None,即yield只有返回值功能,不具备传值功能。
send()方法,接受外部传入的一个变量,并根据变量内容计算结果后返回。如send(i),receive = i,并且返回red给player1_red。
next()相当于send(None)
yield与return
在一个生成器中,如果没有return,则默认执行到函数完毕时返回StopIteration:
如果遇到return,如果在执行过程中 return,则直接抛出 StopIteration 终止迭代。
如果在return后返回一个值,那么这个值为StopIteration异常说明,不是程序的返回值。
import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield
print("包子[%s]来了,被[%s]吃了!" %(baozi,name)) c = consumer("Bob")
c.__next__() b1= "韭菜馅"
c.send(b1) # 包子[韭菜馅]来了,被[Bob]吃了!
c.__next__() # 包子[None]来了,被[Bob]吃了! def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print(name, "开始准备做包子啦!")
for i in range(5):
time.sleep(1)
print("做了1个包子,分两半!")
c.send(i)
c2.send(i) producer("Cat")
总结
按照鸭子模型理论,生成器就是一种迭代器,可以使用for进行迭代。
第一次执行next(generator)时,会执行完yield语句后程序进行挂起,所有的参数和状态会进行保存。再一次执行next(generator)时,会从挂起的状态开始往后执行。在遇到程序的结尾或者遇到StopIteration时,循环结束。
可以通过generator.send(arg)来传入参数,这是协程模型。
可以通过generator.throw(exception)来传入一个异常。throw语句会消耗掉一个yield。可以通过generator.close()来手动关闭生成器。
next()等价于send(None)
参考:http://python.jobbole.com/81911/
迭代器
如果给定一个list或tuple,我们可以通过for
循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。
可以直接作用于for循环的数据类型有以下几种:
集合数据类型:如list、tuple、dict、set、str等;
generator,包括生成器和带yield的generator function.
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable。
可以通过isinstance()来判断一个对象是否是Iterable对象。
from collections import Iterable print(isinstance([],Iterable)) #True
print(isinstance({},Iterable)) #True
print(isinstance("abs",Iterable)) #True
print(isinstance((x for x in range(10)),Iterable)) #True
print(isinstance(100,Iterable)) #False
生成器不但可以通过for循环遍历,还可以被next()函数不断调用并且返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值。
可以被next()函数调用并不断返回下一个值得对象称为迭代器:Iterator。
可以通过isinstance()来判断一个对象是否是Iterator对象。
生成器都是迭代器对象,迭代器不一定是生成器。但是list、dict、str虽然是Iterable,却不是Iterator。
from collections import Iterator
print(isinstance((x for x in range(10)),Iterator)) #True
print(isinstance([],Iterator)) #False
print(isinstance({},Iterator)) #False
print(isinstance("abs",Iterator)) #False
把list、dict、str等Iterable变成Iterator可以使用iter()函数。
print(isinstance(iter([]),Iterator)) #True
print(isinstance(iter({}),Iterator)) #True
print(isinstance(iter("abs"),Iterator)) #True f = iter([1,2,3,4])
print(next(f)) #
print(next(f)) #
print(next(f)) #
list、dict、str等数据类型和Iterator对象的区别
Python的Iterator对象表示一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据抛出StopIteration错误。可以把这个数据流看成一个有序序列,但我们不知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时才会计算。
Iterator可以表示一个无限大的数据流,例如全体自然数,。但是list永远不可能存储全体自然数。
小结
凡是可作用于for
循环的对象都是Iterable
类型;
凡是可作用于next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列;
集合数据类型如list
、dict
、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。
Python的for
循环本质上是通过不断调用next()
函数实现的,例如:
#for循环
for x in [1, 2, 3, 4, 5]:
pass #等价于: # 首先获得Iterator对象:
it = iter([1, 2, 3, 4, 5])
# 循环:
while True:
try:
# 获得下一个值:
x = next(it)
except StopIteration:
# 遇到StopIteration就退出循环
break
Python修炼之路-装饰器、生成器、迭代器的更多相关文章
- Python自动化 【第四篇】:Python基础-装饰器 生成器 迭代器 Json & pickle
目录: 装饰器 生成器 迭代器 Json & pickle 数据序列化 软件目录结构规范 1. Python装饰器 装饰器:本质是函数,(功能是装饰其它函数)就是为其他函数添加附加功能 原则: ...
- Python(四)装饰器、迭代器&生成器、re正则表达式、字符串格式化
本章内容: 装饰器 迭代器 & 生成器 re 正则表达式 字符串格式化 装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解 ...
- python基础-函数之装饰器、迭代器与生成器
1. 函数嵌套 1.1 函数嵌套调用 函数的嵌套调用:在调用一个函数的过程中,又调用了其他函数 def bar(): print("from in the bar.") def f ...
- 跟着ALEX 学python day4集合 装饰器 生成器 迭代器 json序列化
文档内容学习于 http://www.cnblogs.com/xiaozhiqi/ 装饰器 : 定义: 装饰器 本质是函数,功能是装饰其他函数,就是为其他函数添加附加功能. 原则: 1.不能修改被装 ...
- python三大器(装饰器/生成器/迭代器)
1装饰器 1.1基本结构 def 外层函数(参数): def 内层函数(*args,**kwargs); return 参数(*args,**kwargs) return 内层函数 @外层函数 def ...
- Python三大神器:装饰器,迭代器,生成器
一.装饰器 由于一个函数能实现一种功能,现在想要在不改变其代码的情况下,让这个函数进化一下,即能保持原来的功能,还能有新的"技能",怎么办? 现已经存在一个自定义的函数func1, ...
- python第四周:装饰器、迭代器、内置方法、数据序列化
1.装饰器 定义:本质是一个函数,(装饰其他函数)就是为其他函数添加附加功能 原则:不能修改被装饰函数的源代码,不能修改被装饰函数的调用方式 实现装饰器的知识储备: 函数即“变量”.每当定义一个函数时 ...
- python函数:叠加装饰器、迭代器、自定义迭代器、生成式
一.叠加多个装饰器二.迭代器三.自定义迭代器四.xxx生成式 一.叠加多个装饰器 # 加载装饰器就是将原函数名偷梁换柱成了装饰器最内层那个wrapper函数 # 在加载完毕后,调用原函数其实就是在调用 ...
- Python学习之路——装饰器
开放封闭原则:不改变调用方式与源代码上增加功能 ''' 1.不能修改被装饰对象(函数)的源代码(封闭) 2.不能更改被修饰对象(函数)的调用方式,且能达到增加功能的效果(开放) ''' 装饰器 # 把 ...
随机推荐
- Reactjs之静态路由、动态路由以及Get传值以及获取
1.新增知识点 /* react路由的配置: 1.找到官方文档 https://reacttraining.com/react-router/web/example/basic 2.安装 cnpm i ...
- VueLoaderPlugin作用
在webpack配置里加入new VueLoaderPlugin, 在plugin里打断点 然后debug: 在这个地方: 可以发现,在webpack初始化的阶段..webpack.js刚开始执行的时 ...
- python unittest中setUp()和setUpClass()、tearDown()和tearDownClass()之间的区别
setUp():每个测试case运行之前运行tearDown():每个测试case运行完之后执行setUpClass():必须使用@classmethod 装饰器, 所有case运行之前只运行一次t ...
- HCL 试验1
PC端配置:配置ip地址 交换机配置:①创建VLAN system-view vlan 10 vlan 20 ②配置PC端接口 interface gi 1/0/1 port link-type ac ...
- 【HTTP】三、HTTP状态保持机制(Cookie和Session)
前面我们提到HTTP协议的特点:无连接.无状态.无连接带来的时间开销随着HTTP/1.1引入了持久连接的机制得到了解决.现在来关注其"无状态"的特点. 所谓的无状态,就是指 ...
- windows系统安装的两个阶段
windows visa .windows7.windows server 2008系统安装的两个阶段1.Boot.WIM----Windows PE系统.提供了windows7安装向导! 2.Ins ...
- vue2.X + HTML5 plus 拍照和调用设备相册 另附 图片转base64和压缩图片方法
HTML5 部分 <button @click="tesCamera()" type="button" :disabled="isshStatu ...
- 深入理解java:4.1. 框架编程之Spring MVC
说到java的mvc框架,struts2和springmvc想必大家都知道, Spring MVC是当前最优秀的MVC框架,自从Spring 2.5版本发布后,由于支持注解配置,易用性有了大幅度的提高 ...
- ERROR 1709 (HY000): Index column size too large. The maximum column size is 767 bytes.
MySQL版本5.6.35 在一个长度为512字符的字段上创建unique key报错 CREATE DATABASE dpcs_metadata DEFAULT CHARACTER SET utf8 ...
- Spring的核心思想,总结得非常好!
依赖注入是面型接口编程的一种体现,是Spring的核心思想.事实上依赖注入并不是什么高深的技术, 只是被Sping这么以包装就显得有些神秘. 如上代码清单所示,Coder使用Java语言打印hello ...