美文网首页
第四篇 闭包函数、装饰器、迭代器、序列化

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

作者: 张大志的博客 | 来源:发表于2018-06-14 14:17 被阅读0次

    一、知识储备

    一 *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']) #这样就可以在字典中取值了
    

    相关文章

      网友评论

          本文标题:第四篇 闭包函数、装饰器、迭代器、序列化

          本文链接:https://www.haomeiwen.com/subject/xlkyeftx.html