第四篇 闭包函数、装饰器、迭代器、序列化

一、知识储备

一 *args,**kwargs
    def index(name,age):
        print(name,age)
    def wrapper(*args,**kwargs):
        #args=(1,2,2,3,4,4,5),kwargs={'x':1,'y':2}
        index(*args,**kwargs) #index(1,2,2,3,4,4,5,y=2,x=1)
    wrapper(1,2,2,3,4,4,5,x=1,y=2) #不可以这样调用函数,不然调用index函数的时候实参是这样的index(1,2,2,3,4,4,5,y=2,x=1)
    wrapper('egon',age=18) #要这样调用才可以
二 函数对象:函数可被当做数据传递
    - 函数可以当做参数传给另外一个函数
    - 一个函数的返回值也可以是一个函数(打破函数的层级限制)
        def f1():
            def f2():
                print('f2')
            return f2
        f=f1()
        f()
三 名称空间与作用域
    名称空间
        - 分三种
            内置名称空间:python解释器启动则生效
            全局名称空间:执行python文件时生效
            局部名称空间:调用函数时,临时生效,函调用结束则失效

        - 加载顺序:先内置,再全局,最后有可能产生局部
        - 查找名字的顺序:先局部,再全局,最后内置
    作用:
        - 分两种
            全局作用域:全局存活,全局有效
            局部作用域:临时存活,局部有效
    强调:作用关系在函数定义阶段就已经固定,与调用位置无关

二、闭包函数

#闭包函数定义:定义在函数内部的函数,特点是:包含对外部作用域而不是对全局作用域名字的引用,该函数就称之为闭包函数
x=1
def outter():
    x=2
    def inner():
        print(x)
    return inner
f=outter()
def f1():
    x=1000000000
    f()
f1()
运行结果为2,inner函数就是闭包函数,引用的是x=2的作用域,不能引用自己层级的,而是引用自己外层的
#函数体内内部需要一个变量,有两种解决方案
#一种是:以参数的形式传入
from urllib.request import urlopen
def get(url):
    return urlopen(url).read()
print(get('http://www.baidu.com'))
print(get('http://www.baidu.com'))
print(get('http://www.baidu.com'))
#另外一种:包起来
from urllib.request import urlopen
def get(url):        
def inner():
        return urlopen(url).read() #inner函数的返回值是urlopen(url).read(),是爬网页的代码
    return inner
baidu=get('http://www.baidu.com') #baidu等于get函数的返回值innner
print(baidu()) #打印的是调用baidu这个函数,baidu=inner,所以打印的结果是inner函数的返回值urlopen(url).read()
print(baidu())
print(baidu())
这样就不用每次都传参数进行调用函数,两种方式执行的结果是一样的,都是爬百度这个网页,也就是百度的源代码

三、简单装饰器

1、为什么要用装饰器:开放封闭原则,对扩展是开放的,对修改是封闭的
2、什么是装饰器
    - 用来装饰它人,装饰器本身可以是任意可调用对象,被装饰器的对象也可以是任意可调用对象
    - 遵循的原则:1、不修改被装饰对象的源代码 2、不修改被装饰对象的调用方式
    - 目标是:在遵循原则1和2的前提,为被装饰器对象添加上新功能
变量是引用,函数是调用
import time
#
def timmer(func):
    # func=index #最原始的index函数的内存地址
    def inner():
        start_time=time.time()
        func()
        stop_time=time.time()
        print('run time is :[%s]' %(stop_time-start_time))
    return inner

@timmer #index=timmer(index) #index等于timemer函数的返回值inner,@timmer表示将其正下方的index函数当做timmer函数的实参传给timmer,并赋值给index函数
def index():
    time.sleep(3)
    print('welcome to index page')
index() 此处index=inner

四、无参装饰器修订

import time
from functools import wraps

def timmer(func):
    @wraps(func)
    def inner(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs) #func=index,res=index函数的返回值
        stop_time=time.time()
        print('run time is :[%s]' %(stop_time-start_time))
        return res

    return inner

@timmer #timmer函数就是一个装饰器,index=timmer(index)
def index():
    '''
    index function #定义描述信息
    :return:
    '''
    time.sleep(3)
    print('welcome to index page')
    return 123

@timmer #home=timmer(home) #home=inner
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)
    return 456

res=index() # res=inner()
print(res)

res=home('egon') #inner('egon')
print(res)
 
print(index.__doc__) #打印描述信息
print(help(index))

五、无参装饰器之auth

import time
current_status={'user':None,'login_status':False}


def auth(func):
    def inner(*args,**kwargs):
        if current_status['user'] and current_status['login_status']:
            res = func(*args, **kwargs)
            return res #函数遇到return就终止
        name=input('username>>:').strip()
        pwd=input('password>>:').strip()
        if name == 'egon' and pwd == '123':
            print('login successfull')
            current_status['user']=name
            current_status['login_status']=True
            res=func(*args,**kwargs)
            return res
    return inner

@auth #index=auth(index)
def index():
    time.sleep(3)
    print('welcome to index page')
    return 123

@auth
def home(name):
    time.sleep(2)
    print('welcome %s to home page' %name)
    return 456
index()
home('egon')

六、有参装饰器

import time
current_status={'user':None,'login_status':False}
def auth(egine='file'):
    # egine='file'
    def wrapper(func):
        def inner(*args,**kwargs):
            if current_status['user'] and current_status['login_status']:
                res = func(*args, **kwargs)
                return res

            if egine == 'file':
                u='egon'
                p='123'
            elif egine == 'mysql':
                print('mysql auth')
                u = 'egon'
                p = '123'
            elif egine == 'ldap':
                print('ldap auth')
            else:
                pass
            name = input('username>>:').strip()
            pwd = input('password>>:').strip()
            if name == u and pwd == p:
                print('login successfull')
                current_status['user'] = name
                current_status['login_status'] = True
                res = func(*args, **kwargs)
                return res
        return inner
    return wrapper
@auth(egine='ldap') #@wrapper #index=wrapper(index) #index=inner
解释:先执行auth(egine='ldap'),调用这个装饰器函数,这个函数的返回值是wrapper,接着运行@wrapper,index=index=wrapper(index),index=inner
def index():
    time.sleep(3)
    print('welcome to index page')
    return 123
index() #inner()

七、加多个参装饰器

import time
current_status={'user':None,'login_status':False}

def timmer(func):
    def inner(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('run time is :[%s]' %(stop_time-start_time))
        return res

    return inner

def auth(egine='file'):
    # egine='file'
    def wrapper(func):
        def inner(*args,**kwargs):
            if current_status['user'] and current_status['login_status']:
                res = func(*args, **kwargs)
                return res

            if egine == 'file':
                u='egon'
                p='123'
            elif egine == 'mysql':
                u = 'egon'
                p = '123'
            elif egine == 'ldap':
                u = 'egon'
                p = '123'
            else:
                pass
            name = input('username>>:').strip()
            pwd = input('password>>:').strip()
            if name == u and pwd == p:
                print('login successfull')
                current_status['user'] = name
                current_status['login_status'] = True
                res = func(*args, **kwargs)
                return res
        return inner
    return wrapper


#@timmer #放在此处表示统计正下方所有函数的执行时间,包括auth这个装饰器函数的时间
@auth(egine='ldap') #@wrapper #index=wrapper(timmer_inner)
@timmer #index=timmer(index) #装饰器是有顺序的,放在index函数正上方表示统计index函数的执行时间,
def index():
    time.sleep(3)
    print('welcome to index page')
    return 123
index() #inn
此示例中增加timmer和auth两个装饰器

八、迭代器

1 什么叫迭代:迭代是一个重复过程,每次重复都是基于上一次的结果来的
2 为什么要用迭代器?
    l=['a','b','c']
    n=0
    while n < len(l):
        print(l[n])
        n+=1
- 对于序列类型:字符串,列表,元组,可以使用基于索引的迭代取值方式,而对于没有索引的类型,如字典,
集合、文件,这种方式不再适用,于是我们必须找出一种能不依赖于索引的取值方式,这就是迭代器
3 可迭代的对象:只要对象内置有__iter__方法,obj.__iter__
4 迭代器对象:对象既内置有__iter__方法,又内置有__next__,如文件对象
注意:可迭代对象不一定是迭代器对象,而迭代器对象一定是可迭代的对象

'''
#可迭代的对象
# 'hello'.__iter__ 字符串
# [1,2].__iter__列表
# (1,2).__iter__ 元组
# {'a':1}.__iter__  字典
# {1,2,3}.__iter__   集合
#既是可迭代对象,又是迭代器对象
# open('a.txt','w').__iter__  文件类型
# open('a.txt','w').__next__
#迭代器对象的用处
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #获得一个迭代器对象iter_dic
print(iter_dic.__next__())
print(next(iter_dic)) #iter_dic.__next__()等于next(iter_dic)
print(next(iter_dic))
执行结果是
a
b
c
with open('a.txt','r') as f:
    print(next(f))
    print(next(f))
对于文件类型也可以用迭代器,把文件里面的内容取出来。这样就获取到了一个不依赖于索引的取值方式

l=[1,2,3,4,5]
iter_l=l.__iter__() #定义一个迭代器对象
print(iter_l)
print(next(iter_l)) #就可以基于迭代器的next方法进行取值而不依赖于索引
print(next(iter_l))
print(next(iter_l))
执行结果:
<list_iterator object at 0x000002A3C3FC0A90>
1
2
3
#基于迭代器对象的迭代取值(不依赖索引)
dic={'a':1,'b':2,'c':3}
iter_dic=dic.__iter__() #制作成迭代器
while True:
    try:
        i=next((iter_dic))#然后一个一个的取值
        print(i)
    except StopIteration:
        break
这样就不会因为一直next而报错
dic={'a':1,'b':2,'c':3}
for i in dic: #iter_dic=dic.__iter__()
    print(i)
for循环就是基于迭代器的原理实现的
迭代器的优缺点:
    - 优点:
        提供了一种统一的迭代取值方式,该方式不再依赖于索引
        更节省内存,因为同一时间只有next才取一个值
    - 缺点:
        无法统计长度
        一次性的,只能往后走,不能往前退,无法获取指定位置的值
from collections import Iterable,Iterator
print(isinstance('hello',Iterable)) #判断hello这个字符串是否是可迭代的对象
print(isinstance('hello',Iterator)) #判断hello这个字符串是否是迭代器对象
执行结果:
True
False

九、生成器

定义:只要函数内部出现yield关键字,那么再调用该函数,将不会立即执行函数体代码,会得到一个结果,该结果就是生成器对象
def func():
    print('===>first')
    yield 1
    print('===>second')
    yield 2
    print('====>third')
    yield 3
g=func() #调用这个函数,因为代码里面有yield,当调用时会生成生成器,也就是迭代器,g此时就是一个迭代器
print(g)
不会执行函数体代码,会得到如下
<generator object func at 0x0000027AE16EEE60>

#生成器本质就是迭代器
print(next(g)) #执行函数体代码并打印函数的返回值,碰到yield就暂停函数
print(next(g)) #从上次暂停的位置继续执行函数代码。直到遇到yield,并打印函数的返回值
print(next(g))
执行结果
===>first
1
===>second
2
====>third
3
yield的功能:
    - 为我们提供了一种自定义迭代器的方式
    - 对比return,可以返回多次值,挂起函数的运行状态,return只能返回一个值,遇到return函数就停止并退出
def func():
    print('===>first')
    yield 1
    print('===>second')
    yield 2
    print('====>third')
    yield 3
g=func()
for i in g:
    print(i)
执行结果:
===>first
1
===>second
2
====>third
3
def func():
    print('===>first')
    yield 1
    print('===>second')
    yield 2
    print('====>third')
    yield 3
print(next(func()))
print(next(func()))
print(next(func()))
执行结果为
===>first
1
===>first
1
===>first
1
因为每次都生成一个新的生成器,也就是迭代器,所以每次next的时候都是一样的
#自定义功能,可以生成无穷多个值,因为同一时间在内存中只有一个值
def my_range(start,stop,step=1):
    while start < stop:
        yield start
        start+=step
g=my_range(1,5,2) #调用这个函数会生成一个生成器g
print(next(g))
print(next(g))
执行结果为:
1
3
def my_range(start,stop,step=1):
    while start < stop:
        yield start
        start+=step
for i in my_range(1,1000000000000000000000000000000000000000000,step=2):
    print(i)
执行结果会生成无穷多个值,并且不会卡,因为每次只生成一个值在内存中
# tail -f access.log | grep '404'实现此功能
import time
def tail(filepath):
    with open(filepath,'rb') as f:
        f.seek(0,2) #移动到文件的最后一行
        while True:
            line=f.readline()
            if line:
                yield line
            else:
                time.sleep(0.2)
def grep(pattern,lines):
    for line in lines:
        line=line.decode('utf-8')
        if pattern in line:
            yield line

g=grep('404',tail('access.log')) #会生成一个生成器g
for line in g:  #for循环会对生成器一个一个的取值,相当于next的作用
    print(line)
yield可以把一个函数的执行结果制作成一个生成器,然后通过for循环一个一个打印结果
#yield的表达式形式的应用
def eater(name):
    food_list=[]
    print('%s 开动啦' %name)
    while True:
        food=yield food_list #food=‘骨头’
        print('%s 开始吃 %s' %(name,food))
        food_list.append(food)
g=eater('alex')
g.send(None) #next(g)=g.send(None),send有两个作用,一个是给yield传值,一个是next的作用,并且是先传值给yield,再从上次暂停的位置往下走,注意第一次要初始化函数,必须传值为None
print(g.send('骨头')) #执行函数体代码,直到遇到yield就暂停函数,并打印函数的返回值food_list,函数的返回值是yield后面的值,跟return一样,但return只能返回一个值
print(g.send('shi')) #从上次暂停的位置往后走
yield的表达式形式可以从函数外部给函数传多个值
def f1():
    while True:
        x=yield
        print(x)
g=f1()
next(g)
g.send(1)
g.send(1)
g.close() #关闭

十、面向过程编程
强调:面向过程编程绝对不是用函数编程那么简单
面向过程的编程思想:核心是过程二字,过程即解决问题的步骤,即先干什么再干什么
基于该思想去编写程序就好比在设计一条流水线,是一种机械式的编程思想
优点:复杂的问题流程化,进而简单化
缺点:可扩展性差

#实现在a目录中递归寻找文件中含有你好的文件,列出文件的绝对路径
import os
def init(func): #定义装饰器函数
    def inner(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return inner

def search(filepath,target): #找到一个文件路径就往下个阶段传一次
    g = os.walk(filepath)
    for dirname, _, files in g:
        for file in files:
            abs_file_path = r'%s\%s' % (dirname, file)
            target.send(abs_file_path)

@init #opener=init(opener)
def opener(target):
    while True:
        abs_file_path=yield
        with open(abs_file_path,'rb') as f:
            target.send((f,abs_file_path)) #send可以传多个值给yield,但要以列表或元组的形式括起来

@init
def cat(target):
    while True:
        f,abs_file_path=yield
        for line in f: #遍历文件的每一行
            res=target.send((line,abs_file_path)) #target=grep(),生成一个生成器target,res等于grep函数的返回值,也就是yield后面的值tag
            if res: #如果res是真就退出循环,表示文件中有你好了,就不把文件的每一行都读出来了,就证明这个文件中有你好了
                break

@init
def grep(pattern,target):
    tag=False #保证res的初始值是False
    pattern = pattern.encode('utf-8')  #encode是编码,decode是解码,因为line是以二进制格式打开的,而pattern是个”你好”,要将其编码为utf-8才可以,也就是bytes形式才可以进行匹配
    while True:
        line,abs_file_path=yield tag
        tag=False #保证每次进行配置时res都是False
        if pattern in line:
            target.send(abs_file_path)
            tag=True

@init
def printer():
    while True:
        abs_file_path=yield
        print(abs_file_path)

search(r'D:\张大志迈远\pythontest\day4\day4\a',opener(cat(grep('你好',printer()))))

十一、三元表达式

常规的if和else模式
name=input('>>: ')
if name == 'alex':
    print('SB')
else:
    print('NB')
三元表达式,中间是条件判断,条件成立写到左边,条件不成立写到右边,就可以把if和else写到一行去
name = input('>>: ')
print('SB' if name == 'alex' else 'NB')
比较大小的操作就可以这样写了
def my_max(x,y):
    return x if x > y else y
print(my_max(2,3))

十二、列表解析与生成器表达式

egg_list=[]
for i in range(10):
    if i >= 3:
        res='egg%s' %i
        egg_list.append(res)

print(egg_list) 
写成如下格式和上面的结果是一样的
l=['egg%s' %i for i in range(10) if i >= 3] #列表解析,可以使编程变的简单
print(l)
执行结果都是
['egg3', 'egg4', 'egg5', 'egg6', 'egg7', 'egg8', 'egg9']
#生成器表达式
g=('egg%s' %i for i in range(10) if i >= 3) #g就是一个生成器
print(g)
print(next(g))
print(next(g))
执行结果:
<generator object <genexpr> at 0x0000021C1E86DE60>
egg3
egg4
练习:将列表中的名字变成大写,除了以sb结尾的
names=['egon','alex_sb','wupeiqi','yuanhao']
names=[name.upper() for name in names if not name.endswith('sb')]
print(names)

十三、序列化

我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化
user={'name':'egon','pwd':'123'} #user是一个字典
with open('db.txt','w',encoding='utf-8') as f:
    f.write(str(user)) #写到文件中的时候不能以字典的形式,不然会报错,要改成字符串的形式写到文件中
with open('db.txt','r',encoding='utf-8') as f:
    data=f.read()
    print(type(data)) #发现从文件中读的时候也是字符串,这样就不是原来的字典形式了,序列化就是可以把字典保存成一个中间状态,然后读的时候又能从中间状态返回到字典

import json
user={'name':'egon','pwd':'123','age':18}
with open('db.json','w',encoding='utf-8') as f:
    f.write(json.dumps(user)) #把字典以json能够识别的格式写到文件里
写到文件中是如下格式
{"name": "egon", "pwd": "123", "age": 18} #发现单引号都变成双引号了,变成json格式了,注意json格式不能识别单引号
with open('db.json','r',encoding='utf-8') as f:
    data=f.read()
    dic=json.loads(data) #通过json反序列化转化回原来的格式与json.dumps相对
print(dic)
    print(dic['age']) #转化回字典格式后就可以取值了
打印结果:
{'name': 'egon', 'pwd': '123', 'age': 18}
18
可以简写成以下方式
import json
user={'name':'egon','pwd':'123','age':18} #定义一个字典
json.dump(user,open('db1.json','w',encoding='utf-8')) #将字典中的内容以json格式写到文件中

dic=json.load(open('db1.json','r',encoding='utf-8')) #反序列化文件中的内容转化回字典格式
print(dic,type(dic),dic['name']) #这样就可以在字典中取值了
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容