【问题标题】:what happens if you import a decorated python function from another script?如果从另一个脚本中导入修饰的 python 函数会发生什么?
【发布时间】:2019-08-03 02:10:26
【问题描述】:

我正在查看带有装饰器的烧瓶后端中的函数,并考虑将其导入另一个脚本并以不同的方式装饰它。有谁知道当你导入它时会发生什么,装饰器是否与它一起使用?

我查看了this,但它正在讨论更多在同一个脚本中发生的事情。

【问题讨论】:

  • 装饰器没有,但装饰器的result有。装饰只是def funcname(...): ...; funcname = decorator(funcname) 的语法快捷方式。除非装饰器通过新函数显式公开它,否则您无权访问原始的、未装饰的函数。
  • @chepner 除非装饰器通过新函数显式公开它 是什么意思?
  • @cardamom:函数可以有属性,装饰器可以返回一个带有附加属性的新函数对象,其中一个可以是对原始的引用。
  • @cardamom:@functools.wraps decorator utility 正是这样做的,它将wrapper.__wrapped__ 设置为原始函数。

标签: python python-3.x decorator


【解决方案1】:

不,导入装饰函数不会删除装饰器。

导入从源模块的全局命名空间中检索当前对象,装饰函数导致装饰器返回值存储在全局命名空间中。

导入模块主要modulename = sys.modules['modulename'](对于import modulename)和objectname = sys.modules['modulename'].objectname分配(对于from modulename import objectname的语法糖,在任何一种情况下,首先确保sys.modules具有加载所需的模块),模块中的全局变量与模块对象上的属性相同。装饰只是functionname = decorator(functionobject) 的语法糖。

如果需要在导入的函数中添加新的装饰器,只需调用装饰器即可:

from somemodule import somedecoratedfunction

newname_or_originalname = decorator(somedecoratedfunction)

如果导入的修饰函数不适合在新层中再次修饰,或者您想访问原始未修饰函数,请查看对象是否具有__wrapped__ 属性:

from somemodule import somedecoratedfunction

unwrapped_function = somedecoratedfunction.__wrapped__

编写良好的装饰器使用@functools.wraps() decorator,它将该属性设置为指向原始属性:

>>> from functools import wraps
>>> def demodecorator(f):
...     @wraps(f)
...     def wrapper(*args, **kwargs):
...         print("Decorated!")
...         return f(*args, **kwargs)
...     return wrapper
...
>>> @demodecorator
... def foo(name):
...     print(f"Hello, {name or 'World'}!")
...
>>> foo('cardamom')
Decorated!
Hello, cardamom!
>>> foo.__wrapped__('cardamom')
Hello, cardamom!

【讨论】:

    【解决方案2】:

    装饰一个函数

    @some_decorator
    def some_func(...):
        ...
    

    相当于将一个函数应用于另一个对象:

    def some_func(...):
        ...
    
    some_func = some_decorator(some_func)
    

    当你导入模块时,你只能访问到当前绑定到some_func的对象,也就是some_decorator应用于原始函数的返回值。除非返回的 some_decorator 包含对原始未修饰函数的引用,否则您无法从导入的模块访问它。

    暴露原文的例子:

    def some_decorator(f):
        def _(*args, *kwargs):
            # Do some extra stuff, then call the original function
            # ...
            return f(*args, **kwargs)
        _.original = f
        return _
    
    @some_decorator
    def some_func(...):
        ...
    

    当您导入模块时,some_module.some_func 指的是修饰函数,但原始未修饰函数可通过 some_module.some_func.original 使用,但因为编写了修饰器以使其可用。 (正如 Martijn Peters 指出的那样,wraps 装饰器为您做了这些——以及其他一些不错的事情——但装饰器仍然需要使用wraps。)

    【讨论】:

    • 感谢这两个很好的答案,将其标记为更简单,不涉及额外的导入,然后,如果@wraps 必须高于或低于另一个装饰器,则不知道将它们放入的顺序.还有一点是,.__wrapped__ 的实际作用是 UNwrap,这是违反直觉的。
    • 它不会打开任何东西;它指的是在实际上被包装之前得到包装的东西。
    • @cardamom 只知道standard library provides additional functionality 当您使用该属性名称时,使您的装饰器在其他上下文中更有用,例如提供自动完成功能的 IDE。另外,@functools.wraps() 会为您提供更多服务。
    猜你喜欢
    • 1970-01-01
    • 2020-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 2018-09-23
    相关资源
    最近更新 更多