• 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
  • 相关阅读:
    新概念4-38
    新概念4-37
    新概念4-36
    新概念4-35
    国史通鉴-03 天下为私 04
    新概念4-34
    西门子 框架断路器 及其他中低压配电设备资料查询
    OPC UA 的本质
    经典Scout添加等时同步设备
    同步报故障解同步启动
  • 原文地址:https://www.cnblogs.com/xmyzero/p/7110303.html
Copyright © 2020-2023  润新知