python装饰器,迭代器,生成器,协程
python装饰器[1]
首先先明白以下两点
#嵌套函数 def out1(): def inner1(): print(1234) inner1()#当没有加入inner时out()不会打印输出1234,当前层级也就是作用域下必须调用函数才能引用 out1()
#函数作用域 def out2(): a = 'aaa' def inner2(): a = 'bbb' print(a) inner2() print(a) out2()#当相同变量处于不同嵌套函数中,会根据作用函数由内而外查找
装饰器:本质还是一个函数, 在不改变函数调用方式的情况下 对函数进行额外功能的封装,装饰一个函数 转给他一个其他的功能
装饰器的目的:装饰器的原本是用来在项目上线之后,因为已经上线了,大批量改代码的话很麻烦,由此产生了装饰器
- 不能修改被装饰的函数的源代码
- 不能修改被装饰的函数的调用方式
import time def demo(): print("wo shi rainbol") def time1(): start = time.time() demo()#给time1函数增加了一个demo的功能 end = time.time() print(end-start) time1() #这样方式很low,如果有一个添加n个函数添加此功能会很麻烦
import time def demo(): print("wo shi rainbol") def demo2(): print("wo yeshi rainbol") def time1(fuc): start = time.time() fuc()#把之前的demo改成公用的fuc,函数即变量 end = time.time() print(end-start) time1(demo)#通过调用time1方法,fuc赋值成了demo,再调用demo的方法 time1(demo2) #time1(demo。。)
以上完成了对任意函数改变调用方式进行了功能的封装,那如何用不改变调用方式的情况下对其进行功能的封装呢?
import time def demo(): print("wo shi rainbol") def time1(fuc): def inner():#根据之前学过的嵌套函数,增加一层inner方法,把值放入其中 start = time.time() fuc() end = time.time() print(end-start) return inner#返回inner方法,但是没有运行 demo = time1(demo)#time1包含了inner的功能/拿到inner的返回值并且赋值给res,装饰器的目的是不改变变量名所以这边仍然用demo变量赋值 demo()#demo通过括号运行inner方法
#下面通过真正的装饰器可以省去上面的步骤 import time def time1(fuc):#2.time1(demo),这里的fuc相当于形参,其实就是demo
def inner():
start = time.time()
fuc()
end = time.time()
print(end-start)
return inner#3.返回inner函数,但没有执行哦
@time1 #1.python先会整体浏览一遍代码关键字的代码,找到了demo方法上头有装饰
# 先调用装饰器,time1也就是demo = time1(demo),括号中的demo相当于实参
def demo(): print("wo shi rainbol")
# demo = time1(demo) <=====> @time1 去掉@time1在这家这段也是一样的
demo()#4.现在才执行()执行函数
#简单装饰器 import time def simper(fuc): def inner(*args,**kwargs): start = time.time() fuc(*args,**kwargs)#增加的方法 end = time.time() print(end - start) return inner @simper #demo = simper(demo) def demo(name):#定义一个方法 print(name)#增加一个公用的功能点 demo(1)
#高级装饰器 当我们再原来装饰器的业务上再增加一个形参,来判断我们的业务是否符合指定的状态,这需要外面再套一层函数 import time def harder(type): if type == 1: def out(fuc): def inner(*args,**kwargs): start = time.time() fuc(*args,**kwargs) end = time.time() print(end - start) return inner return out elif type == 2: pass @harder(1)#返回out函数 def demo(name): print(name) demo(1)
其他可以参考https://blog.csdn.net/u013471155/article/details/68960244 这个写得很详细
python装饰器[2]
装饰器与之间的迭代
下面是产品四次提出需求后再不修改调用参数使用装饰器在修改lose函数来完成我们业务上的需求,原因是上线项目许多接口都调用我们这个类下的参数,再不破坏接口本身情况下使用装饰器迭代是最佳选择
import time # 现在有一个需求1.0:在整个功能的基础上再添加一个减去4.5的操作使得最后结果为0,前提条件是不修改调用参数 def outer(fuc): def inner(*args, **kwargs): a = fuc(*args, **kwargs) - 4.5 return a return inner # 迭代需求1.1:在整个功能的基础上再添加一个增加10的操作使得最后结果为10,提前条件是不修改调用参数 def outer2(fuc2): def inner2(*args, **kwargs): b = fuc2(*args, **kwargs) + 10 return int(b) return inner2 # 迭代需求1.2:在整个功能的基础上再添加一个时间参数判断lose函数的时间,目前为了模拟添加2秒延迟时间,提前条件是不修改调用参数 def showtime(fuc): def inner3(*args, **kwargs): starttime = time.time() a = fuc(*args, **kwargs) time.sleep(2) endtime = time.time() b = endtime - starttime return a, b return inner3 # 迭代需求2.0:目前项目的lose的方法在业务繁忙时会异常中断,为此接口添加异常处理,提前条件是不修改调用参数 def tryexception(fuc): def tryer(self, *args, **kwargs): try: res = fuc(self, *args, **kwargs) except Exception as e: self.lose() return 'ERROR' return res return tryer '''主程序''' class MyDecorato(object): def __init__(self): pass def chengxu(self, a, b): c = a + b return c @tryexception #4.最后调用tryexception装饰器,装饰器之间调用是从下到上来依次调用 @showtime # 3.调用showtime装饰器 @outer2 # 2.调用outer2装饰器 @outer # 1.先调用outer装饰器 def lose(self, c):#频繁调用的函数lose pingjun = c / 2 return pingjun d = MyDecorato() res1 = d.chengxu(6, 3) res2, time = d.lose(res1) print('最后的值:', res2, '时间:', time)
python装饰器[3]
#wrap函数为functools标准库中模块 def test(): '''i am test''' print('一个简单的实验') test() print(test.__doc__) print(test.__name__) # #>> 一个简单的实验 #>> i am test #>> test print('--------------------------------') def outer(fuc): '''outer is me''' print('this is outer') def inner(*args,**kwargs): '''inner is me''' print('this is inner1') fuc(*args,**kwargs) print('this is inner2') return inner @outer def test(): '''i am test''' print('一个简单的实验') test() print(test.__doc__) print(test.__name__) #>> this is outer #>> this is inner1 #>> 一个简单的实验 #>> this is inner2 #>> inner is me #>> inner print('-----------------------') #如果我们想要得到test里面的数据就要调用一个特定装饰器来帮我们实现 import functools def outer(fuc): '''outer is me''' print('this is outer') # @functools.wraps(fuc) def inner(*args,**kwargs): '''inner is me''' print('this is inner1') fuc(*args,**kwargs) print('this is inner2') #return inner return functools.update_wrapper(inner,fuc)#@functools.wraps(fuc)也可以,update_wrapper是调用其内部wrapper @outer def test(): '''i am test''' print('一个简单的实验') test() print(test.__doc__) print(test.__name__) #>> this is outer #>> this is inner1 #>> 一个简单的实验 #>> this is inner2 #>>> i am test #>> test print('------------------') #保持wrapper和test的属性值一样,这样也可以实现同样的效果 import functools def outer(fuc): '''outer is me''' print('this is outer') def inner(*args,**kwargs): '''inner is me''' print('this is inner1') fuc(*args,**kwargs) print('this is inner2') inner.__doc__ = fuc.__doc__ inner.__name__ = fuc.__name__ return inner @outer def test(): '''i am test''' print('一个简单的实验') test() print(test.__doc__) print(test.__name__)
python装饰器[4]
#通过类中的装饰器实现,普通方式 class Foo(object): def __init__(self): pass def decorator(foo): def inner(self): print('before') foo(self) print('after') return inner @decorator def test(self): print('testing') foo = Foo() foo.test()
#通过类中的装饰器实现,继承方式 class Foo(object): def __init__(self): pass def decorator(self): def inner(*args, **kwargs): print('before') self(*args, **kwargs) print('after') return inner @decorator def test1(self): print('我被执行了') class Foo2(Foo): @Foo.decorator # 执行被继承的方法 def decorator(self): print('执行被继承的方法开始') super(Foo2, self).test1() # 运行Foo2父类Foo的test1方法 print('执行被继承的方法结束') foo = Foo() foo.test1() print('-----') foo2 = Foo2() foo2.decorator()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#实例 class Test1(object): def decorator1(self): def inner(*args, **kwargs): self(*args, **kwargs) print('n年前添加的附加功能') return inner @decorator1 def test1(self): print('n年前实现的某个功能') class Test2(Test1): def decorator2(self): def inner(*args, **kwargs): self(*args, **kwargs) print('今天添加的附加功能') return inner @decorator2 def test2(self): super(Test2, self).test1() print('昨天自己实现的功能') foo = Test2() foo.test2()
#通过类中的装饰器实现,获取对象方法的实例属性 def mod_test(cls): # 返回修改的类 def decorator(fun): # 返回装饰函数 def new_fun(self): print(self.before) print(fun(self)) print(self.after) return new_fun cls.test = decorator(cls.test) return cls @mod_test class Foo(object): def __init__(self): self.before = "before" self.after = "after" def test(self): return "testing" foo = Foo() foo.test()
python迭代器
l = [1,2,3,4,5,6] print(l.__iter__()) #iter(l) 这两者是一样的,都返回了一样迭代器对象 <list_iterator object at 0x00000000023B7080> d = (iter(l)) print((next(d)))#返回 1 print((next(d)))#返回 2 #所以生成器本身就是迭代器 #for循环本身主要做的三件事: for i in [1,2,34,5,5]: # 1. iter([1,2,34,5,5])#调用可迭代对象的iter方法返回一个迭代器对象 # 2.调用迭代器对象的next方法 # 3.处理Stoplteration #校验 from collections import Iterator,Iterable # Iterable 迭代器 # Iterator 迭代对象 print(isinstance([1334],list))#判断给对象是否为一个list,返回布尔值 print(isinstance(l,Iterable))#判断是否是迭代对象,返回布尔值
自定义迭代器
class Mytest: def __init__(self, len): self.index = 0 self.len = len def __iter__(self): return self def __next__(self): if self.index < self.len: self.index += 1 return self.index raise StopIteration for i in Mytest(20): print(i) #打印1-20,迭代器底层调用,结构复杂
python生成器
用法1:
f = (x for x in range(1000))#使用列表生成式外面套了一层中括号并赋值给f对象 print(f)#此时f打印的就是一个生成器对象 <generator object <genexpr> at 0x0000000001DD79E8> #此时需要打印x必须如下方式,生成器就像一位厨师,做出x就是一盘盘菜,每一盘菜必须吃完再吃第二盘,而且不能跳着吃,倒着吃 print(next(f))#调用使用next()比较常见 print(next(f)) print(next(f)) print(f.__next__())#f.__next__()也是可以的 py2的调用方式是f.next直接调用 print(f.__next__()) print(f.__next__())
用法2:
#yield也是生成器中的例子,如果在没有使用next(),函数根本不会被执行,调用每一次程序会检测yield如果有,yield包括后面的代码不会执行,直到下次调用才执行下次的,所以函数中只要有yield就是生成器 #yield可以理解成return def test(len): print(len,'11') yield 1 print(len,'222') yield 2 print(len,'333') test('1')#此函数不会被调用 for i in test('1'):#for内置有生成器next,可以对生成器对象一直使用next(n) print(i,'调用') #打印 # 1 11 # 1 调用 # 1 222 # 2 调用 # 1 333 #这个就是异步io的原理了,python里面的协程基于yield实现
#生成器的好处: #如果我们写一个 danger = [x for x in range(9999999999999999999)] # 当我们打印danger时我们的电脑会在内存中放置0-n的数据,造成内存不足,死机的情况,生成器的出现会把旧变量替换成新变量,从而不会造成大数据所产成内存泄露的问题 nodanger = (x for x in range(999999999999999999999999)) print(next(nodanger))
协程
简单的说只要能够完成多任务切换的都是协程,规避io操作是协程体现出的效果
yield是协程最底层的使用方法
#yield的使用 def f(): print('jjjj') yield 1 print('gggg') yield print(f())#创建一个生成器对象,但是函数不会执行 gen = f() #next(gen)#执行生成器对象 gen.send(None) x = gen.send(10)#next(gen)这两者是一样的 print(x)
gevent模块
#gevent模块 在gevent中主要模式就是greenlet,它是c扩展的轻量级协程 from greenlet import greenlet def test1(): print('111') b.switch() print('333') b.switch() def test2(): print('222') a.switch() print('444') a = greenlet(test1)#创建一个生成器对象 b = greenlet(test2) a.switch()
# from gevent import monkey # monkey.patch_all()#实时监听io堵塞,效果显著,要注意的是这两句话要放到最上面不然就会报错,我也不知道为什么 import gevent def test3(): print('模拟io堵塞1') gevent.sleep(1)#模拟堵塞时间 print('堵塞消除1') def test4(): print('模拟io堵塞2') gevent.sleep(2) print( '堵塞消除2') gevent.joinall([gevent.spawn(test3),gevent.spawn(test4)]) #joinall效果是如果两个方法中其中一个出现io堵塞,会跳到另外一个方法,如果都堵塞都会等着,直到io堵塞消除 #优势:io堵塞的时间取决于io堵塞最长的时间,提升效率
协程实例:
from gevent import monkey
monkey.patch_all()
#gevent模块 #基于greenlet封装,避免多线程切换导致io执行效率降低 import gevent import requests def run(name, url): r = requests.get(url) open(name + '.html', 'wb').write(r.content) url = {'rainbol01': 'https://www.cnblogs.com/RainBol/', 'rainbol02': 'https://www.cnblogs.com/RainBol/p/9505438.html', 'rainbol03': 'https://www.cnblogs.com/RainBol/p/10077388.html' } for name, url in url.items(): g = gevent.spawn(run, name, url) # 启动 g.join() # 等待并切换 #阻塞等待分配任务完成后结束 # l = [] # for i in range(10): # g = gevent.spawn(run,name,url) # l = g.append(g) # g.joinall(l)
https://www.cnblogs.com/RainBol/p/13612932.html 更多协程
版权声明:本文原创发表于 博客园,作者为 RainBol 本文欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则视为侵权。