Python 中的 @wraps 到底是个啥东西?


只要你读得很快

你可能在随意的 Python 代码中见过这个 @wraps 的东西,你可能想知道这到底是什么?

函数有元数据

元数据指的是函数本身的数据。

def apple():
  '''a function that prints apple'''
  print('apple')

print(apple.__name__)  # apple
print(apple.__doc__)   # 打印apple的函数

例子包括函数名 .__name__ 或函数 docstring .__doc__

装饰器如何工作

装饰器用于改变函数的行为方式,而无需修改任何源代码。

def greet(name):
    return 'hello ' + name

print(greet('tom'))  # hello tom

在这里,我们有一个普通的 greet 功能

def add_exclamation(func):
    def wrapper(name):
        return func(name) + '!'
    return wrapper

@add_exclamation
def greet(name):
    return 'hello ' + name

print(greet('tom'))  # hello tom!

我们通过在 greet() 上添加 @add_exclamation 来用 add_exclamation() 来装饰 greet()。这里,add_exclamation 是装饰器,greet 是被装饰的函数。

请注意,greet() 的行为已经改变,而我们根本没有编辑 greet() 的源代码。这是装饰器的功劳。

装饰语法魔术

def add_exclamation(func):
    def wrapper(name):
        return func(name) + '!'
    return wrapper

@add_exclamation
def greet(name):
    return 'hello ' + name

print(greet('tom'))  # hello tom!

这是完全相同的:

def add_exclamation(func):
    def wrapper(name):
        return func(name) + '!'
    return wrapper

def greet(name):
    return 'hello ' + name

greet = add_exclamation(greet)

print(greet('tom'))  # hello tom!

注意 “greet = add_exclamation(greet) ”一行。

装饰会导致元数据丢失

# 没有装饰
def greet(name):
  '''says hello to someone'''
  return 'hello ' + name

print(greet.__name__)  # greet
print(greet.__doc__)   # says hello to someone

在这里,我们可以顺利打印 greet() 的元数据。

# 加了装饰
def add_exclamation(func):
    def wrapper(name):
        return func(name) + '!'
    return wrapper

@add_exclamation
def greet(name):
  '''says hello to someone'''
  return 'hello ' + name

print(greet.__name__)  # wrapper
print(greet.__doc__)   # None

add_exclamation 装饰 greet 后,请注意元数据发生了变化。__name__ 变成了 “wrapper”,而 __doc__ 变成了 wrapper 的 docstring。

这是因为当我们装饰 greet 时,我们实际上是在做这件事:

greet = add_exclamation(greet)

我们正在将 greet 重新分配给一个由 add_exclamation 返回的函数--wrapper

这就是为什么当我们尝试打印 greet.__name__greet.__doc__ 时,会打印 wrapper 的元数据的原因。

@wraps 防止元数据在装饰过程中丢失

from functools import wraps

def add_exclamation(func):
    @wraps(func)
    def wrapper(name):
        return func(name) + '!'
    return wrapper

@add_exclamation
def greet(name):
  '''says hello to someone'''
  return 'hello ' + name

print(greet.__name__)  # greet
print(greet.__doc__)   # says hello to someone

请注意,尽管使用了 ad_exclamation 装饰,greet 的元数据还是回到了正常状态。

更具体地说,@wraps(something)something 的元数据覆盖了函数的元数据。这样,我们原来函数的元数据就不会丢失了。


🏴‍☠️宝藏级🏴‍☠️ 原创公众号『数据STUDIO』内容超级硬核。公众号以Python为核心语言,垂直于数据科学领域,包括可戳👉 PythonMySQL数据分析数据可视化机器学习与数据挖掘爬虫 等,从入门到进阶!

长按👇关注- 数据STUDIO -设为星标,干货速递

相关推荐

  • 一分钟原画变3D角色,清华VAST成果入选图形学顶会SIGGRAPH
  • AI慢思考蒸馏进快思考,Llama2跃升至GPT-4水平,不写过程也能做对题
  • 苏妈掷48亿现金吞下AI模型公司,英伟达有的AMD也要有
  • 程序员如何用好“AI搭子”?实操演示来了,揭秘多元业务场景如何用AI工具提效降本
  • H100利用率飙升至75%!英伟达亲自下场FlashAttention三代升级,比标准注意力快16倍
  • 告警:MyBatis-Plus中慎用@Transactional注解,坑的差点被开了...
  • A800,它真免费送啊!
  • SOA 和微服务有何区别?
  • @Schedule定时任务+分布式环境,这些坑你一定得注意!!!
  • 不服不行,这才是后端API接口应该有的样子!
  • 20 个好看又酷炫的 404 页面【附源码】
  • 中国数据库前世今生:90年代的群雄争霸与技术革新
  • Chrome 浏览器权限升级:竟可访问系统资源!
  • 分享一件有趣的事情,我帮 CPython 修复了一个 bug
  • 如何防止被恶意刷接口?
  • 赛尔笔记 | 具身大模型研究综述
  • 鄂维南院士领衔新作:大模型不止有RAG、参数存储,还有第3种记忆
  • 明显提升Transformer在高频信号的预测效果的策略。
  • [开源]一款快速且灵活的后台框架,可轻松实现复杂页面,内置代码生成器
  • 推荐!神器 Jupyter 的可视化 Debug~