python闭包以及装饰器的基本使用

110 阅读5分钟

闭包

# 在函数嵌套(函数里面再定义函数)的前提下
# 内部函数使用了外部函数的变量(还包括外部函数的参数)
# 外部函数返回了内部函数
# 定义一个外部函数
def func_out(num1):
    # 定义一个内部函数
    def func_inner(num2):
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)

    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner


if __name__ == '__main__':
    # 创建闭包实例
    f = func_out(1)
    # 执行闭包
    f(2)
    f(3)

闭包的基本使用

# 外部函数
def config_name(name):
    # 内部函数
    def say_info(info):
        print(name + ": " + info)

    return say_info

if __name__ == '__main__':
    tom = config_name("Tom")

    tom("你好!")
    tom("你好, 在吗?")

    jerry = config_name("jerry")

    jerry("不在, 不和你玩!")

修改闭包内的外部变量

# 定义一个外部函数
def func_out(num1):

    # 定义一个内部函数
    def func_inner(num2):
        # 这里本意想要修改外部num1的值,实际上是在内部函数定义了一个局部变量num1
        nonlocal num1  # 告诉解释器,此处使用的是 外部变量a
        # 修改外部变量num1
        num1 = 10
        # 内部函数使用了外部函数的变量(num1)
        result = num1 + num2
        print("结果是:", result)

    print(num1)
    func_inner(1)
    print(num1)

    # 外部函数返回了内部函数,这里返回的内部函数就是闭包
    return func_inner

if __name__ == '__main__':
    # 创建闭包实例
    f = func_out(1)
    # 执行闭包
    f(2)

装饰器

# 就是给已有函数增加额外功能的函数,它本质上就是一个闭包函数。
#
# 装饰器的功能特点:
#
# 不修改已有函数的源代码
# 不修改已有函数的调用方式
# 给已有函数增加额外的功能

# 添加一个登录验证的功能
def check(fn):
    def inner():
        print("请先登录....")
        fn()
    return inner


def comment():
    print("发表评论")

## 使用装饰器来装饰函数
comment = check(comment)
comment()

# 装饰器的基本雏形
# def decorator(fn): # fn:目标函数.
#     def inner():
#         '''执行函数之前'''
#         fn() # 执行被装饰的函数
#         '''执行函数之后'''
#     return inner

装饰器的语法糖

# 添加一个登录验证的功能
def check(fn):
    print("装饰器函数执行了")
    def inner():
        print("请先登录....")
        fn()
    return inner

# 使用语法糖方式来装饰函数
@check  #@check 等价于 comment = check(comment) 注意事项:这个模块已加载就会调用装饰器进行加载,就是comment()不调用也会输出:装饰器函数执行了
def comment():
    print("发表评论")


# comment()

装饰器的基本使用

import time

# 装饰器函数
def get_time(func):
    def inner():
        begin = time.time()
        func()
        end = time.time()
        print("函数执行花费%f" % (end-begin))
    return inner


@get_time
def func1():
    for i in range(100000):
        print(i)


if __name__ == '__main__':
    func1()

装饰带有参数的函数

# 添加输出日志的功能
def logging(fn):
    def inner(num1, num2):
        print("--正在努力计算--")
        fn(num1, num2)

    return inner


# 使用装饰器装饰函数
@logging
def sum_num(a, b):
    result = a + b
    print(result)


if __name__ == '__main__':
    sum_num(1, 2)

装饰带有返回值的函数

# 添加输出日志的功能
def logging(fn):
    def inner(num1, num2):
        print("--正在努力计算--")
        result = fn(num1, num2)
        return result
    return inner


# 使用装饰器装饰函数
@logging
def sum_num(a, b):
    result = a + b
    return result


if __name__ == '__main__':
    result = sum_num(1, 2)
    print(result)

装饰带有不定长的参数

# 添加输出日志的功能
def logging(fn):
    def inner(*args, **kwargs):
        print("--正在努力计算--")
        fn(*args, **kwargs)  # 此处一定要自己好好理解理解哦,如果不解构,则传过去的是fn((1,2,3),{"a":1}),这样的参数,不符合sum_num函数的原意

    return inner


# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
    result = 0
    for value in args:
        result += value

    for value in kwargs.values():
        result += value

    print(result)

if __name__ == '__main__':
    sum_num(1, 2, a=10)

通用装饰器

# 添加输出日志的功能
def logging(fn):
    def inner(*args, **kwargs):
        print("--正在努力计算--")
        result = fn(*args, **kwargs)
        return result

    return inner


# 使用语法糖装饰函数
@logging
def sum_num(*args, **kwargs):
    result = 0
    for value in args:
        result += value

    for value in kwargs.values():
        result += value

    return result


@logging
def subtraction(a, b):
    result = a - b
    print(result)


if __name__ == '__main__':
    result = sum_num(1, 2, a=10)
    print(result)

    subtraction(4, 2)

多个装饰器的使用

def make_div(func):
    """对被装饰的函数的返回值 div标签"""

    def inner(*args, **kwargs):
        return "<div>" + func() + "</div>"

    return inner


def make_p(func):
    """对被装饰的函数的返回值 p标签"""

    def inner(*args, **kwargs):
        return "<p>" + func() + "</p>"

    return inner


# 装饰过程: 1 content = make_p(content) 2 content = make_div(content)
# content = make_div(make_p(content))
@make_div
@make_p
def content():
    return "人生苦短"


if __name__ == '__main__':
    result = content()

    print(result)  # <div><p>人生苦短</p></div>
    # 多个装饰器的装饰过程是: 离函数最近的装饰器先装饰,然后外面的装饰器再进行装饰,由内到外的装饰过程

带有参数的装饰器

# 添加输出日志的功能
def logging(flag):

    def decorator(fn):
        def inner(num1, num2):
            if flag == "+":
                print("--正在努力加法计算--")
            elif flag == "-":
                print("--正在努力减法计算--")
            result = fn(num1, num2)
            return result
        return inner

    # 返回装饰器
    return decorator


# 使用装饰器装饰函数
@logging("+")
def add(a, b):
    result = a + b
    return result


@logging("-")
def sub(a, b):
    result = a - b
    return result

if __name__ == '__main__':
    result = add(1, 2)
    print(result)

    result = sub(1, 2)
    print(result)

类装饰器的使用

class Check(object):
    def __init__(self, fn):
        # 初始化操作在此完成
        self.__fn = fn

    # 实现__call__方法,表示对象是一个可调用对象,可以像调用函数一样进行调用。
    def __call__(self, *args, **kwargs):
        # 添加装饰功能
        print("请先登陆...")
        self.__fn()


@Check
def comment():
    print("发表评论")


if __name__ == '__main__':
    comment()


    # @Check
    # 等价于
    # comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
    # 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
    # 在call方法里进行对fn函数的装饰,可以添加额外的功能。