装饰器
装饰器的定义
在不修改原代码和调用的前提下,增加新的功能
装饰器的使用场景
①授权:装饰器能有助于检查是否被授权去使用一个web应用的端点,被大量应用于Falsk和Django web框架
②日志:在记录日志的地方添加装饰器
③缓存:通过装饰器获取缓存中的值
装饰器的原型
1 import time
2 def showtime(func):
3 def wrapper():
4 start_time = time.time()
5 func()
6 end_time = time.time()
7 print('spend is {}'.format(end_time - start_time))
8
9 return wrapper
10
11 def foo():
12 print('foo..')
13 time.sleep(3)
14
15 foo = showtime(foo)
16 foo()
不带参数的装饰器:(装饰器,被装饰函数都不带参数)
1 import time
2 def showtime(func):
3 def wrapper():
4 start_time = time.time()
5 func()
6 end_time = time.time()
7 print('spend is {}'.format(end_time - start_time))
8
9 return wrapper
10
11 @showtime #foo = showtime(foo)
12 def foo():
13 print('foo..')
14 time.sleep(3)
15
16 @showtime #doo = showtime(doo)
17 def doo():
18 print('doo..')
19 time.sleep(2)
20
21 foo()
22 doo()
带参数的被装饰的函数
1 import time
2 def showtime(func):
3 def wrapper(a, b):
4 start_time = time.time()
5 func(a,b)
6 end_time = time.time()
7 print('spend is {}'.format(end_time - start_time))
8
9 return wrapper
10
11 @showtime #add = showtime(add)
12 def add(a, b):
13 print(a+b)
14 time.sleep(1)
15
16 @showtime #sub = showtime(sub)
17 def sub(a,b):
18 print(a-b)
19 time.sleep(1)
20
21 add(5,4)
22 sub(3,2)
带参数的装饰器(装饰函数)
实际是对原有装饰器的一个函数的封装,并返回一个装饰器(一个含有参数的闭包函数),
当使用@time_logger(3)调用的时候,Python能发现这一层封装,并将参数传递到装饰器的环境去
1 import time
2 def time_logger(flag = 0):
3 def showtime(func):
4 def wrapper(a, b):
5 start_time = time.time()
6 func(a,b)
7 end_time = time.time()
8 print('spend is {}'.format(end_time - start_time))
9
10 if flag:
11 print('将此操作保留至日志')
12
13 return wrapper
14
15 return showtime
16
17 @time_logger(2) #得到闭包函数showtime,add = showtime(add)
18 def add(a, b):
19 print(a+b)
20 time.sleep(1)
21
22 add(3,4)
类装饰器:一般依靠类内部的__call__方法*
1 import time
2 class Foo(object):
3 def __init__(self, func):
4 self._func = func
5
6 def __call__(self):
7 start_time = time.time()
8 self._func()
9 end_time = time.time()
10 print('spend is {}'.format(end_time - start_time))
11
12 @Foo #bar = Foo(bar)
13 def bar():
14 print('bar..')
15 time.sleep(2)
16
17 bar()
使用装饰器的缺点:
- 位置错误的代码 不能在装饰器之外添加逻辑功能
- 不能装饰@staticmethod 或者 @classmethod已经装饰过的方法
- 装饰器会对原函数的元信息进行更改,比如函数的docstring,name,参数列表
常用的内置装饰器:
1.staticmethod: 类似实现了静态方法 注入以后,可以直接 : 类名.方法
2.property:经过property装饰过的函数 不再是一个函数,而是一个property,类似实现get,set方法
1 @property
2 def width(self):
3 return self.__width
4
5 @width.setter
6 def width(self, newWidth):
7 self.__width = newWidth
3.classmethod: 与staticmethod很相似,貌似就只有这一点区别:
第一个参数需要是表示自身类的 cls 参数,
可以来调用类的属性,类的方法,实例化对象等。
生成器
生成器的定义:
1、生成器,即生成一个容器。
2、在Python中,一边循环,一边计算的机制,称为生成器。
3、生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他数据类型需要调用自己的内置iter() 方法或__iter__()的内置函数),
所以,生成器就是一个可迭代对象。
生成器的作用:
- 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的。
- 而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
- 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?
- 这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
- 要创建一个generator,有很多种方法,第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
print( [i*2 for i in range(10)] ) #列表生成式
print( (i*2 for i in range(10)) ) #生成器
-
我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?
-
如果要一个一个打印出来,可以通过next()函数获得generator的下一个返回值:
g = (i*2 for i in range(10))
print( g.__next__() ) # 0
print( g.__next__() ) # 2
生成器的工作原理
①生成器是一个函数,可以记住上一次返回时在函数体的位置,而且函数的参数都会保留
②对生成器函数的第二次(或第N次)调用跳转至该函数中间,而上次调用的所有局部变量都保持不变
③生成器不仅“记住”了数据状态,生成器还“记住”了它在流控制构造中的位置
④迭代到下一次的调用时,所使用的参数都是第一次所保留下的,即是说,在整个所有函数调用的参数都是第一次所调用时保留的,而不是新创建的
yield生成器运行机制
(1)在python中,当定义了一个函数,使用yield关键字时,这个函数就是一个生成器,
(2)yield,函数返回的是一个对象,如果想取的值就需要调用next()函数
(3)每当调用一次迭代器的next函数,生成器函数运行到yield之处,返回yield后面的值且在这个地方暂停,所有的状态都会背保持住,直到下次next函数被调用,或者遇到异常循环退出
使用yield函数实现斐波那契数列
def fib(max_num):
a,b = 1,1
while a < max_num:
yield b
a,b=b,a+b
g = fib(10) #生成一个生成器:[2, 3, 5, 8, 13]
print(g.__next__()) #第一次调用返回:1
print(list(g))
yield实现单线程下的并发效果
1、yield相当于 return 返回一个值,并且记住这个返回的位置,下次迭代时,代码从yield的下一条语句开始执行。
2、send() 和next()一样,都能让生成器继续往下走一步(下次遇到yield停),但send()能传一个值,这个值作为yield表达式整体的结果
import time
def consumer(name):
print("%s 准备吃包子啦!" %name)
while True:
baozi = yield
print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
def producer(name):
c = consumer('A')
c2 = consumer('B')
c.__next__()
c2.__next__()
print("老子开始准备做包子啦!")
for i in range(10):
time.sleep(1)
print("做了2个包子!")
c.send(i)
c2.send(i)
producer("alex")
迭代器
迭代器的定义:
迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。使用inter()函数创建迭代器
迭代器和可迭代对象
1. 凡是可作用于for循环的对象都是可迭代的(Iterable)类型;
2. 凡是可作用于next()函数的对象都是迭代器(Iterator)类型,它们表示一个惰性计算的序列;
3. 集合数据类型如list、dict、str等是可迭代的但不是迭代器,不过可以通过iter()函数获得一个Iterator对象。
4. Python的for循环本质上就是通过不断调用next()函数实现的
总结: 一个实现了__iter__方法的对象是可迭代的,一个实现next方法的对象是迭代器
迭代器的两个方法:
1. 迭代器仅是一容器对象,它实现了迭代器协议。它有两个基本方法
㈠ next方法
返回容器的下一个元素
㈡ __iter__方法
返回迭代器自身
2. 迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。
3. __iter__方法会返回一个迭代器(iterator),所谓的迭代器就是具有next方法的对象。
4. 在调用next方法时,迭代器会返回它的下一个值,如果next方法被调用,但迭代器中没有值可以返就会引发一个StopIteration异常
a = iter([1,2,]) #生成一个迭代器
print(a.__next__())
print(a.__next__())
print(a.__next__()) #在这一步会引发 “StopIteration” 的异常
判断是迭代器和可迭代对象
注:列表,元组,字典是可迭代的但不是迭代器
from collections import Iterable
print(isinstance([],Iterable)) #True
print(isinstance({},Iterable)) #True
print(isinstance((),Iterable)) #True
print(isinstance("aaa",Iterable)) #True
print(isinstance((x for x inrange(10)),Iterable)) #True
列表不是迭代器,只有生成器是迭代器
from collections import Iterator
t = [1,2,3,4]
print(isinstance(t,Iterator)) #False
t1 = iter(t)
print(isinstance(t1,Iterator)) #True
自定义迭代器
class MyRange(object):
def __init__(self, n):
self.idx = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.idx < self.n:
val = self.idx
self.idx += 1
return self.n[val]
else:
raise StopIteration()
l = [4,5,6,7,8]
obj = MyRange(l)
print obj.next() # 4
print obj.next() # 5
print obj.next() # 6
迭代器和生成器的区别
(1)在使用生成器时,创建一个函数,在使用迭代器时,使用内置函数iter()和next(),在生成器中,使用关键字‘yield’来每次生成/返回一个对象
(2)每次’yield’暂停循环时,生成器会保存本地变量的状态,而迭代器并不会使用局部变量,只需要一个可迭代对象进行迭代
(3)使用类可以实现迭代器,但无法实现生成器,生成器运行速度快,语法简单,迭代器更能节约内存
list = [1,2,3,4,5] # 列表是一个可迭代对象,不是一个迭代器
print dir(list) # 所以 list 中有 __iter__() 方法,没有 __next__()方法
iter_obj = list.__iter__() # __iter__()方法返回迭代器对象本身(这个迭代器对象就会有 next 方法了)
print '###################################\n'
print iter_obj.next() # 1
print iter_obj.next() # 2
print iter_obj.next() # 3