python装饰器和语法糖

装饰器在Python中是一种强大的工具,用于在不修改原有函数代码的情况下扩展其功能。它们允许我们在函数定义前使用@符号加上装饰器名,实现如日志记录、性能测试、权限控制等功能。本文通过实例详细解释了装饰器的工作原理,包括如何定义和使用装饰器,以及如何创建带有返回值的装饰器和注册装饰器。同时,强调了装饰器在导入时的执行特性及其在实际代码组织中的作用。
摘要由CSDN通过智能技术生成

装饰器在大工程中比较常见,那么如何理解装饰器呢?打个比方,假如你建好了一栋大房子,建好后还想加一些功能,这个时候房子的主体结构是不能动了,只好在现有房子的基础上做一些装饰/装修。这些装饰在不影响/不修改原来房子功能的基础上,增加了美观等功能。

装饰器是可调用的对象,其参数是另一个函数(被装饰的函数)。 装
饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个
函数或可调用对象。[2]

@decorate
def target():
    print('running target()')

def target():
    print('running target()')
target = decorate(target)


上述两种写法的代码效果一样。
上面出现的@符号就是装饰器的语法糖,它放在函数开始定义的地方,这样就可以省略最后一步再次赋值的操作。
注意:Python 中的函数和 Java、C++不太一样,Python 中的函数可以像普通变量一样当做参数传递给另外一个函数,例子如下:

def foo():
    print("foo")

def bar(func):
    func()

foo()
bar(foo)
bar

函数调用:

  • 不带括号时,调用的是这个函数本身 ,是整个函数体,是一个函数对象,不须等该函数执行完成
  • 带括号(参数或者无参),调用的是函数的执行结果,须等该函数执行完成的结果
# 装饰器通常把函数替换成另一个函数
def deco(func):
    def inner():
        print('running inner()')
    return inner # deco 返回 inner 函数对象

@deco
def target(): # 使用 deco 装饰 target。
    print('running target()')

# target = deco(target) # 有了语法糖这句可以省略  相当于 target = wrapper
target() # 调用被装饰的 target 其实会运行 inner。
running target()
target # 审查对象,发现 target 现在是 inner 的引用。
<function __main__.deco.<locals>.inner()>
# 装饰器通常把函数替换成另一个函数
def deco(func):
    def inner():
        print('running inner()')
        return func()
    return inner # deco 返回 inner 函数对象

@deco
def target(): # 使用 deco 装饰 target。
    print('running target()')

# target = deco(target) # 有了语法糖这句可以省略  相当于 target = wrapper
target() # 调用被装饰的 target 其实会运行 inner。
running inner()
<function __main__.target()>
registry = [] # registry 保存被 @register 装饰的函数引用。

def register(func):  # register 的参数是一个函数。
    print('running register(%s)' % func) # 为了演示,显示被装饰的函数。
    # func.__name__ 显示被装饰函数的名字
    registry.append(func)  # 把 func 存入 registry。
    return func # 返回 func:必须返回函数;这里返回的函数与通过参数传入的一样。

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

def f3(): 
    print('running f3()')
    
def main(): # main 显示 registry,然后调用 f1()、f2() 和 f3()。
    print('running main()')
    print('registry ->', registry)
    f1()
    f2()
    f3()
    
if __name__=='__main__':
    main() 
running register(<function f1 at 0x7f7c0c5bf268>)
running register(<function f2 at 0x7f7c0c6b3488>)
running main()
registry -> [<function f1 at 0x7f7c0c5bf268>, <function f2 at 0x7f7c0c6b3488>]
running f1()
running f2()
running f3()

注意,register 在模块中其他函数之前运行(两次)。调用register 时,传给它的参数是被装饰的函数,例如 <function f1 at 0x7f7c0c5bf598>。

上面例子主要想强调,函数装饰器在导入模块时立即执行,而被装饰的函数只在明确调用时运行。这突出了 Python 程序员所说的导入时和运行时之间的区别。

考虑到装饰器在真实代码中的常用方式,上面例子有两个不寻常的地方。

  • 装饰器函数与被装饰的函数在同一个模块中定义。实际情况是,装饰器通常在一个模块中定义,然后应用到其他模块中的函数上。
  • register 装饰器返回的函数与通过参数传入的相同。实际上,大多数装饰器会在内部定义一个函数,然后将其返回。

[1] 廖雪峰 装饰器
[2] 流畅的python

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=29v0kx6sv4w0s

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烤粽子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值