函数的闭包与装饰器

一、函数对象

  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\n' % backend
    # 将用户中的server拼接起来
    old_server_record = '%sserver %s %s weight %s maxconn %s\n' % (' ' * 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\n' % (' ' * 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\n' % 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

 

 

posted @ 2019-11-11 18:43  想吃手抓饼  阅读(131)  评论(0编辑  收藏  举报