day4_装饰器、生成器、迭代器、序列化

装饰器

简单的定义:
为其他函数添加附加功能。


原则:
1、不能修改被参数的函数的源代码。
2、不能修改被装饰的函数的调用方式。


实现装饰器的知识储备:
1、函数即“变量”
2、高阶函数
3、嵌套函数

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


如何理解函数即变量?
这里写图片描述
1在被内存地址中定义,它可以赋值给x,也可以赋值给y,同理,函数(包括匿名函数)的函数体在内存地址中定义,可以把它起名为一个函数名。


高阶函数的特性,我们看看和装饰器有什么区别?
特性1、把一个函数名当做实参传给另外一个函数(在不修改装饰函数源代码的情况下为其添加)
特性2、返回值中包含函数名(不修改函数的调用方式)
举例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:liyanan

import time

def bar():
    time.sleep(3)
    print("in the bar")

def udp1(func):
    start_time = time.time()
    func()  #run bar
    stop_time = time.time()
    print("the func run time is %s"%(stop_time-start_time))

udp1(bar)

#输出
in the bar
the func run time is 3.0001718997955322

可以看出运行bar函数时,”udp1(bar)”,还是改变了调用方式。不过已经很像装饰器了,这怎么解决呢?


我们加入函数的嵌套

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:liyanan


import time

def timer(replace): #replace 代替
    def deco():
        start_time = time.time()
        replace()
        stop_time = time.time()
        print("the replace run time is %s"%(start_time-stop_time))
    return deco

@timer #udp1=timer(udp1)
def udp1():
    time.sleep(1)
    print("in the udp1")

udp1()
#输出
in the udp1
the replace run time is -1.0002222061157227

这样一个装饰器就完成了。


带参数的装饰器

import time

def timer(replace): #replace 代替
    def deco(*args,**kwargs):
        start_time = time.time()
        replace(*args,**kwargs)
        stop_time = time.time()
        print("the replace run time is %s"%(start_time-stop_time))
    return deco
@timer
def udp2(name,age):
    print("upd2",name,age)
udp2(name="li",age="24")
#输出
upd2 li 24
the replace run time is 0.0

装饰器的语法
被装饰函数的正上方,单独占一行。

@timer 
def udp1():
    time.sleep(1)
    print("in the udp1")

udp1()

生成器

在认识生成器之前,先认识下列表生成式:

a = [i+1 for i in range(10)]
print(a)
#输出
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

生成器的定义和应用场景:
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。

所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器:generator。

创建一个生成器,把列表生成式最外边的[],换成(),就创建好了。

a = (i+1 for i in range(10))
print(a)

print(next(a))
print(next(a))
#输出
<generator object <genexpr> at 0x02A552D0>
1
2

可以看出生成器a是内存中一个地址,获取生成器的下一个元素,可以用next方法。但只能一个一个获取,想要一次性获取更多的元素,可以使用for循环。

a = (i+1 for i in range(10))
print(a)


for j in a:
    print(j)
#输出
<generator object <genexpr> at 0x033852D0>
1
2
3
4
5
6
7
8
9
10

用函数生成斐波拉契数列(除第一个和第二个数外,任意一个数都可由前两个数相加得到:1, 1, 2, 3, 5, 8, 13, 21, 34, …)。

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:liyanan

def fib(max):
    n, a, b, = 0, 0, 1
    while n<max:
        print(b)
        a, b = b, a+b   
        #相当于
        #t = (b, a + b)  t是一个tuple
        #a = t[0]
        #b = t[1]
        n+=1
    return 'done'

fib(20)
#输出
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765

生成器的另一种定义方法,使用函数,区别是把print(b)改为yield b。

def fib(max):
    n, a, b, = 0, 0, 1
    while n<max:
        #print(b)
        yield b
        a, b = b, a+b
        n+=1
    return 'done'

for j in fib(20):
    print(j)
#输出
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
676

在把函数变成生成器后,我们基本上从来不会用next()来获取下一个返回值,而是直接使用for循环来迭代。

使用yield实现单线程下生成器的并行。
例子:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:liyanan

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("liyanan")

#输出
A准备吃包子了
B准备吃包子了
开始做包子了
做了2个包子
包子0来了,被A吃了
包子0来了,被B吃了
做了2个包子
包子1来了,被A吃了
包子1来了,被B吃了
做了2个包子
包子2来了,被A吃了
包子2来了,被B吃了
做了2个包子
包子3来了,被A吃了
包子3来了,被B吃了
做了2个包子
包子4来了,被A吃了
包子4来了,被B吃了
做了2个包子
包子5来了,被A吃了
包子5来了,被B吃了
做了2个包子
包子6来了,被A吃了
包子6来了,被B吃了
做了2个包子
包子7来了,被A吃了
包子7来了,被B吃了
做了2个包子
包子8来了,被A吃了
包子8来了,被B吃了
做了2个包子
包子9来了,被A吃了
包子9来了,被B吃了

迭代器

可以用于for循环的数据类型有以下几种:
1、集合数据类型,如list、tuple、dict、set、str等。
2、生成器generator。
这些可以直接作用for循环的对象统称为可迭代对象:Iterable。可以使用isinstance()判断一个对象是否是Iterable对象:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:liyanan

from collections import Iterable

print(isinstance([],Iterable))
print(isinstance(100,Iterable))
#输出
True
False

在这些可迭代对象中,生成器生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,像这样可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator。可以使用isinstance()判断一个对象是否是Iterator对象:

from collections import Iterator

print(isinstance([],Iterator))
print(isinstance((x for x in range(10)),Iterator))
#输出
False
True

但是可以用iter()函数把把list、dict、str等Iterable变成Iterator

print(isinstance(iter([]),Iterator))
小结

凡是可作用于for循环的对象都是Iterable类型;

凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;

集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

Python的for循环本质上就是通过不断调用next()函数实现的。


目录的简要说明

目录组织方式
假设你的项目名为foo,

Foo/
|– bin/
| |– foo
|
|– foo/
| |– tests/
| | |– init.py
| | |– test_main.py
| |
| |– init.py
| |– main.py
|
|– docs/
| |– conf.py
| |– abc.rst
|
|– setup.py
|– requirements.txt
|– README

bin/: 存放项目的一些可执行文件,当然你可以起名script/之类的也行。
foo/: 存放项目的所有源代码。(1) 源代码中的所有模块、包都应该放在此目录。不要置于顶层目录。(2) 其子目录tests/存放单元测试代码; (3) 程序的入口最好命名为main.py。
docs/: 存放一些文档。
setup.py: 安装、部署、打包的脚本。
requirements.txt: 存放软件依赖的外部Python包列表。
README: 项目说明文件。


目录的调用和切换:

这里写图片描述

import os
import sys

base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

sys.path.append(base_dir)

import conf ,core

序列化和反序列化

将系统或者状态存入现有的内存和文件中,使用json和pickle,json和pickle的区别是pickle可以处理存储特殊的内容比如函数和类等。json只能处理字符串,字典,列表等。


先看json
序列化:

import json

info = {
    'name':'li',
    'age':22,

}

f = open("test.txt","w")
f.write(json.dumps(info))
f.close()

反序列化:

import json

f = open("test.txt","r")
data = json.loads(f.read())
print(data)

再来看pickle
序列化:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
#Author:liyanan

import pickle

def sayhi(name):
    print("hello,",name)

info = {
    'name':'li',
    'age':22,
    'func':sayhi
}

f = open("test.txt","wb")
f.write(pickle.dumps(info))
f.close()

反序列化:

import pickle

def sayhi(name):  #定义的函数序列化的时候已经被释放,所以要重新定义
    print("hello2,",name)

f = open("test.txt","rb")
data = pickle.loads(f.read())
print(data['func']("li"))
#输出
hello2, li
None

注意用法:
1、序列化的时候可以写成

pickle.dump(info,f) #f.write( pickle.dumps( info) )

2、反序列化的时候可以写成

data = pickle.load(f) #data = pickle.loads(f.read())

3、要序列化多个字符串或者列表,函数等的时候,注意dump一次load一次,不能dump多个,这样load会报错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值