python之闭包和装饰器

函数嵌套

内部函数使用外部函数变量

外部函数返回内部函数

简单示例

def outer(num1):
    def inner(num2):
        result=num1+num2
        print(result)
    return inner
inner=outer(1)
inner(2)
#或者outer(1)(2)

外部函数调用返回的是内部函数,再执行

  • 闭包可以保存外部函数内的变量,不会随着外部函数调用完而销毁。

注意点:

  • 由于闭包引用了外部函数的变量,则外部函数的变量没有及时释放,消耗内存。
def work(name):
    def say(msg):
        print(f'{name}说:{msg}')
    return say
work('张三')('在吗')
work('李四')('我再')
'''
张三说:在吗
李四说:我再

'''

内部函数修改外部函数的变量

def outer(num1):
    def inner(num2):
        # 修改外部函数的变量nonlocal 而不是 global 
        nonlocal num1
        num1=10
        print(num1+num2)
    return inner
outer(5)(5)
# 15

装饰器

装饰器就是一个闭包函数,而装饰器外部函数传入的是需要装饰的函数,并再装饰器的内部函数调用

作用 就是再不改变函数的同时对其进行功能上的扩展!

如函数只有发表功能此时加上登录后再执行此函数

def decorate(func):
    print('装饰器开始执行')
    def inner():
        print('登录')
        func()
    return inner
def comment():
    print('发表功能')
comment=decorate(comment)
comment()
'''
装饰器开始执行
登录
发表功能
'''

分析:

        ·comment=decorate(comment)  comment()

          装饰器函数传入需要装饰的comment 并放回内部函数 然后内部函数再去执行

        comment=decorate(comment) 且只执行这句不执行comment()运行发现

        装饰器开始执行  这句话执行了 此时comment就是inner函数 都执行需要再次调用inner()

语法糖简写

def decorate(func):
    print('装饰器开始执行')
    def inner():
        print('登录')
        func()
    return inner
@decorate  #此语法糖代替了下面注释的那句话
def comment():
    print('发表功能')
# comment=decorate(comment)
comment()
'''
装饰器开始执行
登录
发表功能
'''

以上只有函数被标记装饰器语法糖,不写调用comment(),运行也会执行外部函数中的内容

计算下某函数的耗时

import time


def decorate(func):
    def inner():
        begin=time.time()
        func()
        end=time.time()
        print(end-begin)
    return inner
@decorate
def fun():
    for i in range(10000):
        print(i)
fun()

装饰带有参数的函数

def decorate(fn):
    def inner(a,b):
        fn(a,b)
    return inner
@decorate
def sum(a,b):
    print(a+b)
sum(5,3)
# 分析:
# 1.sum有参数,当该函数当作参数传进装饰器中,那么此时内部函数调用时也要带上该参数,
# 2.若该调用有参数,内部函数也要有该参数,此时调用sum(5,3)

装饰带有返回值的函数

def decorate(fn):
    def inner(a,b):
        return fn(a,b)
    return inner
@decorate
def sum(a,b):
    return a+b
print(sum(5,3))

装饰带有不定长参数的函数

tup = (1, 2, 3)
a, b, c = tup
print(a, b, c)  # 1 2 3

dic = {'name': 'znd', 'age': 18}
d, e = dic
print(d, e)  # name age


# 不定长参数使用元组和字典形式接收需要加上* **

def decorate(fn):
    def inner(*args, **kwargs):
        print('努力计算中...')
        fn(*args, **kwargs)

    return inner


# 对不定长参数的数进行累加计算
@decorate
def sum(*args, **kwargs):
    result = 0
    for v in args:
        result += v
    for v in kwargs.values():
        result += v
    print(result)


sum(1, 2, 3, a=10, b=5)
#努力计算中...
# 21

通用装饰器

# 添加输出日志的功能
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)

result = sum_num(1, 2, a=10)
print(result)

subtraction(4, 2)

运行结果:

--正在努力计算--
13
--正在努力计算--
2

 总之函数结构什么样子的 内部函数就应该是什么样的!

多个装饰器的使用示例代码

def make_div(fn):
    def inner():
        return "<div>" + fn() + "</div>"

    return inner


def make_p(fn):
    def inner():
        return '<p>' + fn() + '</p>'

    return inner


@make_p
@make_div
def content():
    return '人生苦短,我用python'


print(content())
#<p><div>人生苦短,我用python</div></p>

先执行最近一个装饰器,执行完毕后再执行其他的 按照顺序依次类推

跟@装饰写的顺序有关

带有参数的装饰器

装饰器参数只能传入函数,此时若想额外带有其他参数,就再此装饰器外层包裹一个函数,

此函数带有该参数,且该函数返回装饰器

# 装饰器外层的函数携带参数
def logging(flag):
    # 装饰器
    def decorate(fn):
        def inner(*args, **kwargs):
            if flag == '+':
                print('正在努力加法计算')
            elif flag == '-':
                print('正在努力减法计算')
            return fn(*args, **kwargs)

        return inner

    # 返回装饰器
    return decorate


@logging('+')
def add(a, b):
    return a + b


@logging('-')
def sub(a, b):
    return a - b


print(add(4, 5))
print(sub(5, 1))
'''
正在努力加法计算
9
正在努力减法计算
4
'''

类装饰器

class Check:
    def __init__(self,fn):
        self.__fn=fn

    def __call__(self, *args, **kwargs):
        print('增加的功能')
        self.__fn()
@Check
def comment():
    print('发表评论')
comment()
# comment=Check(comment)
# comment()
# 增加的功能
# 发表评论

分析:

之前是函数装饰器的时候,当@装饰器的时候就会自动调用该装饰器,

类自动调用时机就是init函数,此时将需要被装饰的函数传进去,

另外类想函数一样 函数()形式直接调用,需要用到call方法。

此时需要再call方法中调用fn()

  • @Check 等价于 comment = Check(comment), 所以需要提供一个init方法,并多增加一个fn参数。
  • 要想类的实例对象能够像函数一样调用,需要在类里面使用call方法,把类的实例变成可调用对象(callable),也就是说可以像调用函数一样进行调用。
  • call方法里进行对fn函数的装饰,可以添加额外的功能。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值