装饰器,生成器,迭代器

装饰器

装饰器的定义
在不修改原代码和调用的前提下,增加新的功能

装饰器的使用场景
①授权:装饰器能有助于检查是否被授权去使用一个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__()的内置函数),
所以,生成器就是一个可迭代对象

生成器的作用:

  1. 通过列表生成式,我们可以直接创建一个列表,但是,受到内存限制,列表容量肯定是有限的。
  2. 而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
  3. 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?
  4. 这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。
  5. 要创建一个generator,有很多种方法,第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
print( [i*2 for i in range(10)] )  #列表生成式          
print( (i*2 for i in range(10)) )  #生成器       
  1. 我们可以直接打印出list的每一个元素,但我们怎么打印出generator的每一个元素呢?

  2. 如果要一个一个打印出来,可以通过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
发布了25 篇原创文章 · 获赞 11 · 访问量 916

猜你喜欢

转载自blog.csdn.net/weixin_45139342/article/details/104212149