Python 函数装饰器和闭包

函数装饰器和闭包

函数装饰器用于在源码中“标记”函数,以某种方式增强函数的行为,用闭包实现。

nolocal是在Python3.0新增的保留关键字。

闭包除了在装饰器中有用之外,还回调式一步编程和函数式编程风格的基础。

装饰器基础,何时执行装饰器

装饰器是可调用对象,参数是另一个函数(被装饰的函数)。

函数装饰器再导入模块是立即执行,而被装饰的函数只有在调用时运行。

装饰器通常在一个模块中定义,然后应用到其他模块中的函数上。

registry = []


def register(func):
    print('running :{}'.format(func))
    registry.append(func)
    return func


@register
def f1():
    print('f1 running.')


@register
def f2():
    print('f2 running.')


def f3():
    print('f3 running.')


def main():
    print(registry)
    f1()
    f2()
    f3()


if __name__ == '__main__':
    main()
打印
running :<function f1 at 0x034F9618>
running :<function f2 at 0x034F96A8>
[<function f1 at 0x034F9618>, <function f2 at 0x034F96A8>]
f1 running.
f2 running.
f3 running.

以上可以看到,在加载模块时,函数装饰器立即执行。被装饰的函数在调用时执行。

示例中的装饰器原封不动的返回被装饰的函数,这种并不是没有用途,比如在Python web框架中,这样的装饰器把函数添加到某种中央注册处,实现路由功能。

使用装饰器实现“策略”模式

使用装饰器,这种方案有几个优点:

  • 促销策略函数无需特殊的名称(不需要以Promo结尾),或不需要放到指定模块中
  • @promotion 装饰器,还便于临时禁用某个促销策略,只需要把装饰器去掉。
  • 促销策略装饰器可以在其他模块中定义,在系统的任何地方都行,只需要导入后装饰。

示例,使用装饰器完成策略模式(部分代码)

promos = []


def promotion(func):
    promos.append(func)
    return func


@promotion
def FidelityPromo(order):
    """具体策略:有1000积分以上的顾客,整个订单可以享受5%的折扣"""
    return order.total() * 0.05 if order.customer.fidelity >= 1000 else 0


@promotion
def BulkItemPromo(order):
    """具体策略:同一个订单中,单个商品的数量达到20个以上,单品享受10%折扣"""
    discount = 0
    for item in order.cart:
        if item.quantity >= 20:
            discount += item.total() * .1
    return discount


@promotion
def LargeOrderPromo(order):
    """具体策略:订单中不同商品的数量达到10个以上,整个订单享受7%折扣"""
    return order.total() * 0.07 if len({item.product for item in order.cart}) >= 10 else 0


def best_promo(oder):
    """选择最佳的折扣"""
    return max(promo(oder) for promo in promos)

变量作用域

这个是一个让人吃惊的示例

b = 2


def f(a):
    print(a)
    print(b)
    b = 2


f(1)
打印
1
Traceback (most recent call last):
  File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 53, in <module>
    f(1)
  File "C:/Users/lijiachang/PycharmProjects/collect_demo/test2.py", line 49, in f
    print(b)
UnboundLocalError: local variable 'b' referenced before assignment

如果在函数内不加b = 2 程序可以正常执行,因为b会找到全局变量中的b=2

但是在函数最后加上了b=2,在Python编译函数的定义体时,判断出b是局部变量,因为在函数内给它赋值了,就不会获取全局变量。

比较字节码来论证:

dis模块为反汇编Python函数字节码提供了简单的方式。

def f2(a):
    print(a)
    print(b)


from dis import dis

print(dis(f2))

结果可以看到a为LOAD_FAST 本地名称,b为LOAD_GLOBAL全局名称。

b = 2


def f2(a):
    print(a)
    print(b)
    b =2


from dis import dis

print(dis(f2))

可以看到这次b是LOAD_FAST 本地名称

另外补充:

LOAD_FAST(var_num)

将对本地co_varnames [var_num]的引用压入堆栈。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python函数装饰器是一种特殊类型的函数,它可以用来修改其他函数的功能。装饰器可以在不修改被装饰函数源代码的情况下,为其添加额外的功能。装饰器本质上是一个返回函数函数,它使用了闭包的概念。 Python提供了@符号作为装饰器的语法糖,使得应用装饰器更加方便。使用装饰器时,可以直接在被装饰函数的定义上方使用@符号加上装饰器函数的名称,就可以将被装饰函数作为参数传递给装饰器函数进行处理。 函数装饰器的应用场景很广泛,它可以用于添加日志记录、性能分析、权限验证、缓存等功能。通过使用装饰器,我们可以将这些功能从原始函数中分离出来,提高代码的可读性和可维护性。 以下是一个简单的示例,演示了如何使用函数装饰器: ``` def decorator(func): def wrapper(*args, **kwargs): # 添加额外的功能 print("装饰器添加的功能") # 调用原始函数 return func(*args, **kwargs) return wrapper @decorator def my_function(): print("原始函数") my_function() ``` 在上面的例子中,我们定义了一个装饰器函数`decorator`,它接受一个函数作为参数,并返回一个新的函数`wrapper`。在`wrapper`函数中,我们可以添加额外的功能,并调用原始函数。通过使用`@decorator`语法,我们将`my_function`函数传递给装饰器进行处理。 当我们调用`my_function()`时,实际上调用的是装饰器返回的`wrapper`函数。在执行`wrapper`函数之前,会先执行装饰器添加的额外功能,然后再调用原始函数。 这就是Python函数装饰器的基本原理和用法。通过使用装饰器,我们可以轻松地修改函数的功能,使代码更加简洁和可重用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值