Python_04

本节内容

  • 装饰器
  • 迭代器
  • 生产器
  • 序列化与反序列化

 1、装饰器 

装饰器的本质是函数,目的是为(装饰其他的函数)为其它函数添加附加功能。

原则

1、不能修改本装饰的函数的源代码

2、不能修改被装饰的函数的调用方式

实现装饰器的知识储备:

1、函数即“变量”

 1 def bar():
 2     print('in the bar')
 3 def foo():
 4     print('in the foo')
 5     bar()
 6 
 7 foo()
 8 # 输出
 9 in the foo
10 in the bar
11 
12 
13 def foo():
14     print('in the foo')
15     bar()
16 def bar():
17     print('in the bar')
18 
19 foo()
20 # 输出
21 in the foo
22 in the bar
23 
24 
25 def foo():
26     print('in the foo')
27     bar()
28 
29 foo()
30 
31 def bar():
32     print('in the bar')
33 
34 # 输出
35 in the foo
36 NameError: name 'bar' is not defined #出现报错

上述代码中,bar和foo表示函数名,相当于函数变量的门牌号,每个门牌号对应一个内存地址,地址里面包含了对应的函数内容。

bar()和foo()表示执行函数,执行函数的过程就是先找到函数名(门牌号),然后通过门牌号找到其对应的内存地址并执行地址中的函数内容。

所以当我们在执行foo()的过程中,首先要满足函数已经被定义,即存在,并存入了相应的内存地址,准备被调用。所以找到内存地址中对应的函数内容:

print('in the foo')

bar()

会调用,并依次执行。此时,会先打印出,内容in the foo;其次,会执行bar(),这时就需要函数bar已存在,如果在执行bar()时函数还未生成,则会出现报错。

这也是为什么第一组和第二组中foo()可以成功执行,而第三组不能执行。因为在执行foo()的过程中,调用内容的过程中,bar()对应的函数bar还未被定义。

另:插播一则内容——匿名函数

匿名函数就是不需要显式的指定函数

1 #这段代码
2 def calc(n):
3     return n**n
4 print(calc(10))
5  
6 #换成匿名函数
7 calc = lambda n:n**n
8 print(calc(10))

匿名函数主要是和其它函数搭配使用的呢,如下:

 1 res = map(lambda x:x**2,[1,5,7,4,8])
 2 for i in res:
 3     print(i)
 4 
 5 # 输出
 6 1
 7 25
 8 49
 9 16
10 64

2、高阶函数:

  • 把一个函数名当做一个实参传递给另外一个函数(在不修改被装饰函数的情况下为其添加功能)
  • 返回值中包含函数名(不修改函数的调用方式)
 1 import time
 2 
 3 def bar():
 4     time.sleep(3)
 5     print('in the bar')
 6 
 7 def func_2(fun):
 8     print(fun)
 9     return fun
10 
11 print(bar)
12 # 输出
13 <function bar at 0x100662e18>
14 
15 bar = func_2(bar)
16 # 输出
17 <function bar at 0x100662e18>
18 
19 bar()  # run bar
20 in the bar #三秒后显示

3、嵌套函数

 1 # 嵌套函数
 2 def foo():
 3     print('in the foo')
 4     def bar():
 5         print('in the bar')
 6     bar()
 7 
 8 foo()
 9 # 输出
10 in the foo
11 in the bar

高阶函数+嵌套函数==>>装饰器

 1 import time
 2 
 3 def bar():
 4     time.sleep(3)
 5     print('in the bar')
 6 
 7 def func(fun):
 8     start_time = time.time()
 9     fun()  # run bar
10     stop_time = time.time()
11     print('the running time of fun is %s' %(stop_time - start_time))
12 
13 func(bar)
14 # 输出
15 in the bar
16 the running time of fun is 3.0043821334838867

以上代码已经是一个简单的高阶函数+嵌套函数版的弱鸡装饰器了,但是:

写代码要遵循开发封闭原则,虽然在这个原则是用的面向对象开发,但是也适用于函数式编程,简单来说,它规定已经实现的功能代码不允许被修改,但可以被扩展,即:

  • 封闭:已实现的功能代码块
  • 开放:对扩展开发

so。。。

 1 import time
 2 
 3 def timer(func):
 4     def warpper():
 5         start_time = time.time()
 6         func()
 7         stop_time = time.time()
 8         print('the func run time is %s' %(stop_time-start_time))
 9     return warpper
10 
11 
12 @timer # time1 = timer(time1)
13 def time_1():
14     time.sleep(3)
15     print('in the time_1')
16 
17 time_1()
18 # 输出
19 in the time_1
20 the func run time is 3.0014379024505615
21 
22 # 也可以传入参数
23 import time
24 
25 def timer(func):
26     def deco(*args,**kwargs):
27         start_time = time.time()
28         func(*args,**kwargs)
29         stop_time = time.time()
30         print('the func run time is %s' % (stop_time - start_time))
31     return deco
32 
33 @timer  # time2 = timer(time2) = deco time2()=deco()
34 def time2(*args,**kwargs):
35     print('time2:',args,kwargs)
36 
37 time2('XMY',age=22,job='IT')
38 print(timer(time1))
39 # 输出
40 time2: ('XMY',) {'age': 22, 'job': 'IT'}
41 the func run time is 9.608268737792969e-05
42 <function timer.<locals>.deco at 0x102693e18>
View Code

 2、迭代器 

迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退。另外,迭代器的一大优点是不要求事先准备好整个迭代过程中所有的元素。迭代器仅仅在迭代到某个元素时才计算该元素,而在这之前或之后,元素可以不存在或者被销毁。这个特点使得它特别适合用于遍历一些巨大的或是无限的集合,比如几个G的文件

特点:

  1. 访问者不需要关心迭代器内部的结构,仅需通过next()方法不断去取下一个内容
  2. 不能随机访问集合中的某个值 ,只能从头到尾依次访问
  3. 访问到一半时不能往回退
  4. 便于循环比较大的数据集合,节省内存
 1 # 可以直接作用于for循环的对象统称为可迭代对象: Iterable
 2 
 3 from collections import Iterable
 4 isinstance([], Iterable)
 5 
 6 # 可以被next()函数调用并不断返回下一个值的对象称为迭代器: Iterator
 7 # Iterator可以表示一个无限大的数据流
 8 from collections import Iterator
 9 isinstance([], Iterator)
10 isinstance(iter([]), Iterator)
11 
12 print(globals())

 3、生成器 

定义:一个函数调用时返回一个迭代器,那这个函数就叫做生成器(generator),如果函数中包含yield语法,那这个函数就会变成生成器 .

yield的主要效果是可以使函数中断,并保存中断状态,中断后,代码可以继续往下执行,过一段时间还可以再重新调用这个函数,从上次yield的下一句开始执行。

 1 # 列表生成式
 2 l = [i ** 2 for i in range(10)]
 3 print(l)
 4 # 输出
 5 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
 6 
 7 '''
 8 相当于
 9 a = []
10 for i in range(10):
11     a.append(i**2)
12 
13 print(a)
14 '''
15 
16 # 生成器
17 # 只有在调用时才回生产相应的数据
18 # 只记录当前位置
19 # 只有一个__next__()方法
20 g = (i ** 2 for i in range(10))
21 print(g)
22 # 输出
23 <generator object <genexpr> at 0x102155620>
24 
25 for i in g:  # g.__next__()
26     print(i)
27 # 输出
28 0
29 1
30 4
31 9
32 16
33 25
34 36
35 49
36 64
37 81
38 
39 # Fibonacci
40 def fib(max):
41     n, a, b = 0, 0, 1
42     while n < max:
43         # print(b)
44         yield b
45         a, b = b, a + b
46         '''
47         相当于
48         t = (b, a + b)
49         a = t[0]
50         b = t[1]
51         '''
52         n = n + 1
53     return 'done'
54 
55 
56 f = fib(10)
57 while True:
58     try:
59         x = next(f)
60         print('f:',x)
61     except StopIteration as e: 当生成器取完时中断、避免报错
62         print('Generator return value:', e.value)
63         break
64 # 输出
65 f: 1
66 f: 1
67 f: 2
68 f: 3
69 f: 5
70 f: 8
71 f: 13
72 f: 21
73 f: 34
74 f: 55
75 Generator return value: done
View Code

另外,还可通过yield实现在单线程的情况下实现并发运算的效果

 1 # 协程
 2 import time
 3 def consumer(name):
 4     print('%s 准备吃包子了' %name)
 5     while True:
 6         baozi = yield
 7         print('包子[%s]来了,被[%s]吃了' %(baozi,name))
 8 
 9 def producer(name):
10     c1 = consumer('A')
11     c2 = consumer('B')
12     c1.__next__()
13     c2.__next__()
14     print('老子要开始做包子了!')
15     for i in range(10):
16         time.sleep(1)
17         print('做了2个包子!')
18         c1.send(i)
19         c2.send(i)
20 
21 producer('Xmy')
View Code

 4、序列化&反序列化 

如果我们要存储和读取一个列表或字典类型的文件,根据之前所学的知识,只能通过如下方法:

 1 info = {
 2     'name':'xmy',
 3     'age':24,
 4 } 5  6 
 7 f = open('test.txt','w')
 8 f.write(str(info)) # 以字符串格式写入文件
 9 f.close()
10 
11 f = open('test.txt','r')
12 data = eval(f.read()) # 读入文件,并转为对应dict格式
13 f.close

除了这种比较麻烦的蠢办法以外,我们还可以用json和pickle两种方法,将对文件进行读取操作

1. json

 1 import json
 2 
 3 info = {
 4     'name':'xmy',
 5     'age':24,
 6 }
 7 
 8 f = open('test.txt','w')
 9 json.dump(info,f) #写入文件,等同于f.write(json.dumps(info))
10 f.close()
11 
12 f = open('test.txt','rb')
13 data = json.load(f)  #读取文件,等同于data = json.loads(f.read())
14 f.close()

2. pickle

pickle的操作和json是一致的,唯一的区别在于,pickle是将原文件内容转为二进制写入文件中,在文件读取中也是从二进制转回。因此在文件操作时,需要标注'rb'、'wb'、'ab'。

此外,json无法对函数进行文件存储,而pickle可以.

 1 import pickle
 2 
 3 def sayhi(name):
 4     print('hello',name)
 5 
 6 info = {
 7     'name':'xmy',
 8     'age':24,
 9     'func':sayhi,
10 }
11 
12 f = open('test.txt','wb')
13 pickle.dump(info,f)  # f.write(pickle.dumps(info))
14 f.close()

但是,在读取的过程中,我们是否可以直接调用该函数呢?

 1 import pickle
 2 
 3 f = open('test.txt','rb')
 4 
 5 data = pickle.load(f)  # data = pickle.loads(f.read())
 6 
 7 f.close()
 8 
 9 # 输出
10 AttributeError: Can't get attribute 'sayhi' on <module '__main__' #出现报错

可见,当初存入后,文件内只是存在了函数形式的字符串,当读取时,由于不存在该函数,所以会产生报错,但如果我们读取之前,再次定义一下sayhi函数,情况就不一样了。

 1 import pickle
 2 
 3 def sayhi(name):
 4     print('hello world',name)
 5 
 6 
 7 f = open('test.txt','rb')
 8 
 9 data = pickle.load(f)  # data = pickle.loads(f.read())
10 
11 f.close()
12 print(data['func']('Xmy'))
13 
14 # 输出
15 hello world Xmy
16 None
原文地址:https://www.cnblogs.com/xmyzero/p/7110303.html