zoukankan      html  css  js  c++  java
  • 函数的闭包与装饰器

    一、函数对象

      python一切皆对象,所以函数也是对象,也可以当做数据被处理

    • 函数可以被引用
    • 可以当作参数传递
    • 返回值可以是函数
    • 可以当作容器类型的元素

    二、函数闭包

    Python 支持函数式编程,所以存在闭包,闭包是由函数及其相关的引⽤环境组合⽽成的实体 , ⼀句话: 闭包 = 函数+引⽤环境,函数式编程中,当内嵌函数体内引⽤到体外的变量 时, 将会连同这些变量(引⽤环境)和内嵌函数体, ⼀块打包成⼀个整体返回。

    如果在一个函数的内部定义了另一个函数,外部的我们叫它为外函数,内部的我们叫它内函数,那么闭包就是在一个外函数中定义了一个内函数,内函数里引用了外函数的临时变量,并且外函数的返回值是内函数的引用。

    x = 1
    def out_func():
        x =2
        def in_func():
            print('in the in_func', x)
        return in_func
    
    f = out_func()
    f()
    # 通过调用__closure__属性查看闭包所包裹的外部变量
    print(f.__closure__, f.__closure__[0].cell_contents)

    """ 
    in the in_func 2
    (<cell at 0x00A27810: int object at 0x1D9DE320>,) 2
    """

    三、装饰器----闭包的运用

      1 . 什么是装饰器? 

    装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
    强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
    装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能

       2 . 装饰器的使用

    • 装饰器的框架   

    def ver_fun(func):
        def wrapper(*args, **kwargs): # 引用外部函数变量func
            # 需要增添的功能逻辑
            func() #这里就是将一开始没有装饰的test运行一次
        return wrapper
    
    
    @ver_fun  # 实际上就相当于 test = ver_fun(test),此时的test就是return回来的wrapper
    def test():
        print('hello world!')
    test() # 实际上就是wrapper()
    • 无惨装饰器 
    mport time
    
    
    def timmer(func):
        def wrapper():
            start_time = time.time()
            func()
            stop_time = time.time()
            print('函数运行的时间为%s' % (stop_time - start_time))
        return wrapper
    
    
    @timmer
    def test():
        time.sleep(2)
        print('执行完毕')
    
    test()
    
    """
    执行完毕
    函数运行的时间为2.000253677368164
    """
    View Code

                  当我们调用test函数时,实际上就是在调用wrapper函数,所以当我们的test函数有参数时,wrapper函数也要带上参数,为了统一,我们一般都会在wrapper函数和func函数上加上*args+**kwargs组合,即上述代码可以修改为

    import time
    
    
    def timmer(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            stop_time = time.time()
            print('函数运行的时间为%s' % (stop_time - start_time))
    
        return wrapper
    
    
    @timmer
    def test(name, age):
        time.sleep(2)
        print('执行完毕')
    
    
    test("jiang", 8)
    View Code
    • 带参数的装饰器

       是为装饰器提供多样功能选择的实现提供的,实现原理是三层闭包。如当我们需要不同分认证方式时,可以使用有参装饰器

    import os
    
    
    def file_handle(backend_data, res=None, type='fetch'):
        '''
        这是一个文件处理的函数,对查找和修改功能中的文件处理部分进行的程序解耦
        :return:
        '''
        if type == 'fetch':
            with open('haproxy.conf', 'r') as read_f:
                tag = False
                ret = []
                for each_line in read_f:
                    if each_line.strip() == backend_data:
                        tag = True
                        continue
                    if tag and each_line.startswith('backend'):
                        break
                    if tag:
                        print(each_line, end='')
                        ret.append(each_line)
            return ret
        elif type == 'change':
            with open('haproxy.conf', 'r') as read_f, 
                    open('haproxy.conf_new', 'w') as write_f:
                tag = False
                write_tag = True
                for each_line in read_f:
                    if each_line == backend_data:
                        tag = True
                        continue
                    if tag and each_line.startswith('backend'):
                        tag = False
                    if not tag:
                        write_f.write(each_line)
                    elif write_tag:
                        for record in res:
                            write_f.write(record)
                        write_tag = False
        os.rename('haproxy.conf', 'haproxy.conf.bak')
        os.rename('haproxy.conf_new', 'haproxy.conf')
        os.remove('haproxy.conf.bak')
    
    
    def fetch(data):
        backend_data = 'backend %s' % data
        return file_handle(backend_data)
    
    
    def add():
        pass
    
    
    def change(data):
        '''
        这是修改功能,用户传入一个列表,列表第一个元素为原修改数据,第二个元素为修改后的数据
        :param data: 用户输入的列表
        :return:
        '''
        # 提取出用户输入数据中的地址
        backend = data[0]['backend']
        backend_data = 'backend %s
    ' % backend
        # 将用户中的server拼接起来
        old_server_record = '%sserver %s %s weight %s maxconn %s
    ' % (' ' * 8, data[0]['record']['server'],
                                                                       data[0]['record']['server'],
                                                                       data[0]['record']['weight'],
                                                                       data[0]['record']['maxconn'])
    
        new_server_record = '%sserver %s %s weight %s maxconn %s
    ' % (' ' * 8, data[1]['record']['server'],
                                                                       data[1]['record']['server'],
                                                                       data[1]['record']['weight'],
                                                                       data[1]['record']['maxconn'])
        print('用户想要修改的记录是', old_server_record)
        print('用户想要修改后的记录是', new_server_record)
        res = fetch(backend)
        print(res)
        if not res or old_server_record not in res:
            return '用户要修改的数据不存在'
        else:
            index = res.index(old_server_record)
            res[index] = new_server_record
    
        res.insert(0, '%s
    ' % backend_data)
        file_handle(backend_data, res=res, type='change')
    
    
    def delete():
        pass
    
    
    if __name__ == '__main__':
        msg = '''
        weibo:查询
        2:添加
        3:修改
        4:删除
        5:退出
        '''
    
        menu_dic = {'weibo': fetch, '2': add, '3': change, '4': delete}
    
        while True:
            print(msg)
            user_choice = input("请输入选项:").strip()
            if not user_choice: continue
            if user_choice == '5': break
    
            user_data = input("请输入你想要的数据:").strip()
            if user_choice != 'weibo':
                user_data = eval(user_data)
            res = menu_dic[user_choice](user_data)
            print(res)
    View Code
    • wraps 的使用

       我们这里调用help方法查看一下test函数

    print(help(test))
    
    """
    Help on function wrapper in module __main__:
    
    wrapper(*args, **kwargs)
    
    None
    """

    可以看到test函数的函数名为wrapper,证明装饰过后的函数函数名和函数的文档搜发生了变化,如果想保留原有函数的属性,除了我们自己手动在装饰器中修改wrapper的__name__和__doc__属性外,我们还可以调用functools模块提供的装饰器来实现,具体如下:

    import time
    from functools import wraps
    
    
    def timmer(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)
            stop_time = time.time()
            print('函数运行的时间为%s' % (stop_time - start_time))
    
        return wrapper
    
    
    @timmer
    def test(name, age):
        time.sleep(2)
        print('执行完毕')
    
    
    # test("jiang", 8)
    print(help(test))
    
    """
    Help on function test in module __main__:
    
    test(name, age)
    
    None
    """
    View Code
  • 相关阅读:
    教你在Linux用飞信(fetion)免费发短信
    date 和 hwclock 命令 (RTC用的着)
    同学会催生“恐聚族” 攀比成风成为炫耀展示会
    35款基于terminal的Linux应用
    探访山东各地旱情 田里麦苗用手一搓成粉末
    多城市近期将出台楼市限购细则 全面限购第三套
    Linux下刻录光盘—cdrecord
    RHEL 6教程:使用本地光盘做yum源
    RHEL6 root登陆问题
    初学者学习LINUX之困惑?方向迷失?GUI?
  • 原文地址:https://www.cnblogs.com/liangweijiang/p/11837432.html
Copyright © 2011-2022 走看看