如果从另一个脚本导入修饰的 python 函数会发生什么?

car*_*mom 4 python decorator python-3.x

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

我看过这个,但它更多地讨论了同一脚本中发生的事情。

Mar*_*ers 6

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

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

导入模块主要modulename = sys.modules['modulename']是(for import modulename) 和objectname = sys.modules['modulename'].objectname赋值(for from modulename import objectname,在这两种情况下首先确保已加载所需模块之后)的语法糖sys.modules,模块中的全局变量与模块对象上的属性相同。装饰只是 的语法糖functionname = decorator(functionobject)

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

from somemodule import somedecoratedfunction

newname_or_originalname = decorator(somedecoratedfunction)
Run Code Online (Sandbox Code Playgroud)

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

from somemodule import somedecoratedfunction

unwrapped_function = somedecoratedfunction.__wrapped__
Run Code Online (Sandbox Code Playgroud)

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

>>> 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!
Run Code Online (Sandbox Code Playgroud)


che*_*ner 5

装饰函数

@some_decorator
def some_func(...):
    ...
Run Code Online (Sandbox Code Playgroud)

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

def some_func(...):
    ...

some_func = some_decorator(some_func)
Run Code Online (Sandbox Code Playgroud)

当您导入模块时,您所能访问的只是当前绑定到的对象,它是应用于原始函数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(...):
    ...
Run Code Online (Sandbox Code Playgroud)

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

  • @cardamom 只知道当您使用该属性名称时,[标准库提供了附加功能](https://docs.python.org/3/library/inspect.html#inspect.unwrap),使您的装饰器在以下方面更加有用其他上下文,例如提供自动完成功能的 IDE。另外,“@functools.wraps()”会为您处理更多事情。 (2认同)