Python学习笔记_1_基础_7:装饰器、生成器、迭代器

一、装饰器

1、什么是装饰器

  1)定义:本质就是函数;目的是为其他函数添加附加功能。

  2)原则:1、不能修改被装饰函数的源代码。
         2、不能修改被装饰函数的调用方式。
      #即:装饰器对被修改函数是完全透明的。
  3)知识点:
      # 1、函数即‘变量’
      # 2、高阶函数
      # 3、嵌套函数
  4)装饰器 <= 高阶函数 + 嵌套函数。

2、函数即“变量”

与其他高级语言类似,Python不支持函数在未声明之前,对其进行引用或者调用。与变量类似,函数定义也是存在定义和调用两个过程。

 1 # 定义变量
 2 a = 1
 3 #使用变量
 4 print(a)
 5 
 6 #定义函数
 7 def test():
 8     print('in the fnc test.')
 9 
10 #调用函数
11 print(test)     #函数引用
12 test()
13 
14 test1 = test   #函数可像变量一样传递
15 print(test1)
16 test1()
 1 print('函数调用顺序'.center(50,'-'))
 2 def foo():
 3     print('in the fnc doo')
 4     bar()
 5 
 6 def bar():
 7     print('in the fnc bar')
 8 
 9 foo()
10 #只需在调用前声明了,foo和bar函数即可,与顺序无关。
11 #调试一次即可发现。

3、高阶函数

  1)、把一个函数当做实参传递给另外一个函数。
  2)、返回值是一个函数。

满足以上任意条件,这个函数就是高阶函数。

 1 import time
 2 def bar():
 3     time.sleep(1)
 4     print('in the fnc bar')
 5 
 6 #1、把一个函数当做实参传递给另外一个函数
 7 def f_gj1(fnc):
 8     s_time = time.time()
 9     fnc()
10     e_time = time.time()
11     print('the fnc [%s] running time is %s'%(fnc,e_time-s_time))
12 
13 def f_gj2(fnc):
14     print(fnc)
15     return fnc
16 
17 #1、拓展了函数功能,没有修改源代码;但是修改了函数调用方式。
18 f_gj1(bar)
19 
20 #2、不修改函数调用方式,没有修改源代码;但是貌似附加功能有限。
21 bar = f_gj2(bar)
22 bar()

4、嵌套函数

在函数体内,申明一个函数,这个函数就是嵌套函数。

 1 def foo():
 2     print('in the fnc foo')
 3     def bar():
 4         print('in the fnc bar')
 5 
 6     bar()
 7 
 8 foo()
 9 
10 #foo1不是嵌套函数
11 #错误示范
12 def foo1():
13     print('in the fnc doo')
14     bar1()
15 
16 def bar1():
17     print('in the fnc bar')

foo是嵌套函数,foo1不是。

5、装饰器使用

 1 import time
 2 #高阶函数(返回一个高阶函数(参数传递形式))
 3 def timer(fnc):
 4     def deco():
 5         s_time = time.time()
 6         res = fnc()
 7         e_time = time.time()
 8         print('the fnc running time is %s'%(e_time-s_time))
 9         return res
10     return deco
11 
12 @timer  # bar = timer(bar)  -->  bar = timer(bar) = deco
13 def bar():
14     time.sleep(1)
15     print('in the fnc bar')
16     return 'fnc res from bar'
17 
18 print(bar())
19 #调试一次,更加透彻理解。
20 #完美,附加了功能,没有修改源代码,没有修改调用方式。但是,函数有参数时,出错!
装饰器v1版
 1 import time
 2 #高阶函数(返回一个高阶函数(参数传递形式))
 3 def timer(fnc):
 4     def deco(*args,**kwargs):
 5         s_time = time.time()
 6         res = fnc(*args,**kwargs)
 7         e_time = time.time()
 8         print('the fnc running time is %s'%(e_time-s_time))
 9         return res
10     return deco
11 
12 @timer  # bar = timer(bar)  -->  bar = timer(bar) = deco
13 def bar():
14     time.sleep(1)
15     print('in the fnc bar')
16     return 'fnc res from bar'
17 
18 @timer # test5 = timer(test5)  -->  test5 = timer(test5) = deco
19 def test5(x,y=2,*args,**kwargs):
20     print(x,y)
21     print(args)
22     print(kwargs)
23     return 'fnc res from test5!'
24 
25 
26 print(bar())
27 print('分割线'.center(50,'-'))
28 print(test5(1,3,4,5,6,name='a',age=8))
29 #完美解决,参数问题。
装饰器v2版
之前的装饰器都是只能实现一种功能,若需要根据条件确定其附近功能呢?
初始网站不需要登陆都能使用,后续需要用户登陆才能查看部分页面。
其中,index 不需要登陆,home本地登陆验证,bbs:ldap远程登陆验证(不知道这是什么毛线)
#带参数的装饰器:加多一层函数嵌套
_username = 'cz'
_password = '123'
def login_type(l_type):
    print('login type: ',l_type)
    def login(fnc):
        def deco(*args,**kwargs):
            if l_type == 'local':
                username = input('username: ')
                password = input('password: ')
                if _username == username and _password == password:
                    print('Welcome user {name} login...'.format(name=username))
                    res = fnc()
                    return res
                else:
                    print('Invalid username or password!')
            elif l_type == 'ldap':
                print('ldap 搞毛线,直接上吧。。。')
                res = fnc()
                return res
        return deco
    return login

def index():
    print('welcome to index page!'.rjust(50,'.'))

@login_type('local')   # login_type('local') & home = login(home) = deco
def home():
    print('welcome to home page!'.rjust(50,'.'))

@login_type('ldap')
def bbs():
    print('welcome to bbs page!'.rjust(50,'.'))


index()
home()
bbs()
装饰器高潮版

装饰器相关代码,应加断点,全部调试一次。可加深理解。

 二、生成器

1、什么是生成器

 1 #通过列表生成式,我们可以直接创建一个列表。
 2     # 但是,受到内存限制,列表容量肯定是有限的。
 3     # 而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,
 4             #  如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
 5             # 所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?
 6             # 这样就不必创建完整的list,从而节省大量的空间。
 7 # 在Python中,这种一边循环一边计算的机制,称为生成器:generator。
 8     # 要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
 9 a = [ i+1 for i in range(10)]
10 print(a)
11 
12 b = ( i+1 for i in range(10))
13 print(b)
14 
15 print(b.__next__())
16 print(b.__next__())
17 
18 #b[5]  #TypeError: 'generator' object is not subscriptable
19 #generator保存的是算法:每次调用next,就计算出下一个元素的值,直到计算到最后一个元素;
20     # 没有更多的元素时,抛出StopIteration的错误。
21 #正确的方法是使用for循环,因为generator也是可迭代对象。
22 print('----------------------')
23 for i in b:
24     print(i)
25 print('----------------------')
26 for i in b:
27     print(i)

2、生成器:函数实现

 1 #斐波那契函数
 2 def fib(max):
 3     n,a,b = 0,0,1
 4     while n< max:
 5         print(b)
 6         a,b = b,a+b
 7         #等价于
 8         # t = (b,a+b)
 9         # a = t[0]
10         # b = t[1]
11         n += 1
12     return '----done------'
13 
14 print('fib函数'.center(50,'-'))
15 print(fib(10))
16 
17 print('fib generator'.center(50,'-'))
18 #定义generator的另一种方法:如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator。
19 def g_fib(max):
20     n,a,b = 0,0,1
21     while n< max:
22         yield b
23         a,b = b,a+b
24         #等价于
25         # t = (b,a+b)
26         # a = t[0]
27         # b = t[1]
28         n += 1
29     return '----done------'
30 #generator和函数的执行流程不一样。
31     # 函数是顺序执行,遇到return语句或者最后一行函数语句就返回。
32     # generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
33 g = g_fib(10)
34 print(g)
35 
36 print(g.__next__())
37 print(g.__next__())
38 print('走个神,干点别的事。。。')
39 print(g.__next__())
40 #基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代:
41 print('for 循环迭代。。。。。')
42 for i in g:
43     print(i)
44 
45 #但是用for循环调用generator时,拿不到generator的return语句的返回值。
46     # 如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中:
47 print('fib generator 获取返回值'.center(50,'-'))
48 g1 = g_fib(10)
49 while True:
50     try:
51         print(g1.__next__(),', ',end= '')
52     except StopIteration as e:
53         print('\nGenerator return value:', e.value)
54         break

3、生成器:牛逼的作用,实现协程并行运算

 1 #通过生成器实现协程并行运算
 2 #生产者消费者模型
 3 import time
 4 def consumer(name):
 5     print("%s 准备吃包子啦!" %name)
 6     while True:
 7        baozi = yield
 8 
 9        print("包子[%s]来了,被[%s]吃了!" %(baozi,name))
10 
11 
12 def producer(name):
13     c = consumer('A')   #创建生成器
14     c2 = consumer('B')
15     c.__next__()        #第一次走到yield
16     c2.__next__()
17     print("老子开始准备做包子啦!")
18     for i in range(10):
19         time.sleep(1)
20         print("做了2个包子!")
21         c.send(i)
22         c2.send(i)
23 
24 
25 # c = consumer('cz')
26 # c.__next__()
27 # b1 = '韭菜包子'
28 # c.send(b1)
29 # c.__next__()
30 
31 producer("alex")
生产者消费者模型

三、迭代器

1、迭代器与可迭代对象

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

  例如:一类是集合数据类型,如list、tuple、dict、set、str等;

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

1 from collections import Iterable
2 print(isinstance([],Iterable))
3 print(isinstance(range(10), Iterable))

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

1 #可以使用isinstance()判断一个对象是否是Iterator对象:
2 from collections import Iterator
3 print(isinstance((x for x in range(10)), Iterator))  #True
4 print(isinstance(range(10), Iterator))  #False

2、可迭代对象 —> 迭代器

 1 # 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。
 2 
 3 #示例:
 4 for x in [1, 2, 3, 4, 5]:
 5     pass
 6 
 7 # 首先获得Iterator对象:
 8 it = iter([1, 2, 3, 4, 5])
 9 # 循环:
10 while True:
11     try:
12         # 获得下一个值:
13         x = next(it)
14         print(x)
15     except StopIteration:
16         # 遇到StopIteration就退出循环
17         break

猜你喜欢

转载自www.cnblogs.com/104cz/p/9656248.html