【问题标题】:The decorated function is not passed to the decorator [duplicate]装饰功能未传递给装饰器[重复]
【发布时间】:2023-03-28 16:08:01
【问题描述】:

以下是一个无用的示例,表明我无法用my_decorator 装饰我的add 函数,并提供一些额外的参数:

def my_decorator(fn=None, message=None, **options):
    assert fn is not None, 'fn is not found'
    print('function is found')
    print(message)
    return fn

# This syntax works (but does not make sense...)
@my_decorator
def multiply(a, b):
   return a * b

# This syntax does not work
@my_decorator(message='surprise')
def add(a, b):
   return a + b

# This no sugar-syntax works
add = my_decorator(add, message='surprise')  # Works

在第二个示例中,fn 被视为None,这引发了AssertionError,但使用“无糖”语法!

我的代码有什么问题? (Python 2.7。是的,我们将迁移它...)

【问题讨论】:

  • 其实你不需要装饰器而是装饰器工厂:一个创建装饰器的函数。
  • @TimothéDelion “如果我不使用 @- 语法,装饰器就可以工作!” ——因为这只是一个普通的函数调用,那为什么行不通呢?相比之下,装饰器语法具有 Laurent 所指出的特定约束。
  • 这篇文章真的很有用:stackoverflow.com/q/739654/1513933
  • 这篇文章,对面hynek.me/articles/decorators,对@wraps非常批评

标签: python decorator


【解决方案1】:

首先,创建装饰器的最低要求是实现一个函数(装饰器),该函数以函数为参数。

例如,你可以这样写:

def my_decorator(f):
    def wrapper(*args, **kwargs):
        print("f() is called")
        return f(*args, **kwargs)

    return wrapper


# You can use your decorator w/o parameter:
@my_decorator
def multiply(a, b):
    return a * b

你得到:

multiply(5, 8)
f() is called
40

如您所见,它可以工作,但您的装饰器不带任何参数。 所以你需要创建一个装饰器工厂:

def my_decorator_factory(message):
    def my_decorator(f):
        def wrapper(*args, **kwargs):
            print("f() is called, saying: " + message)
            return f(*args, **kwargs)

        return wrapper

    return my_decorator


# You can use your decorator factory with a parameter:
@my_decorator_factory(message="hi")
def multiply(a, b):
    return a * b

你得到:

multiply(5, 8)
f() is called, saying: hi
40

但是,您希望能够像装饰器一样调用装饰器工厂(无参数)。

因此,您需要更改装饰器工厂的签名以允许以下调用:

my_decorator_factory(message="something")  # or
my_decorator_factory(function)

在这里,您需要检查第一个参数是否是其他函数:

import inspect


def my_decorator_factory(message=None):
    if inspect.isfunction(message):
        return my_decorator_factory(message=None)(message)
    else:
        def my_decorator(f):
            def wrapper(*args, **kwargs):
                print("f() is called, saying: " + (message or "(empty)"))
                return f(*args, **kwargs)

            return wrapper

        return my_decorator


@my_decorator_factory
def multiply(a, b):
    return a * b


@my_decorator_factory(message='surprise')
def add(a, b):
    return a + b

你得到:

multiply(5, 8)
f() is called, saying: (empty)
40
add(3, 2)
f() is called, saying: surprise
5

您可以考虑使用Wrapt 来实现“好的”装饰器/装饰器工厂。

【讨论】:

  • 我认为使用my_decorator_factory() 会更明智,而不是通过检查你的方式来使用my_decorator_factory 但失去装饰不是函数的可调用对象的能力。跨度>
  • 嗯,有几种检查方法,您也可以使用callable 谓词。实际上,你也可以装饰类(但这是另一个讨论)。
【解决方案2】:

my_decorator(message='surprise') 返回None,因为它隐式使用了fn=None。 然后,您尝试将None 应用到add,您注意到这是行不通的。

您需要一个返回装饰器的参数化函数。非正式地,这被称为参数化装饰器。

这是一个例子:

def make_decorator(message):
    def decorator(func):
        def new_func(*args, **kwargs):
            print(message)
            return func(*args, **kwargs)
        return new_func
    return decorator

@make_decorator(message='surprise')
def add(a, b):
    return a + b

x = add(1, 2) # prints 'surprise' and sets x to 3

注意

@make_decorator(message='surprise')
def add(a, b):
    return a + b

等价于

def add(a, b):
    return a + b

decorator = make_decorator(message='surprise')
add = decorator(add)

【讨论】:

  • 你的回答不完整,multiply呢?见my answer
  • 你是对的@Laurent LAPORTE。我的实际需要是能够使用相同的装饰器进行my_decorator_factory(message="something") # 或my_decorator_factory(function) 这两个操作。这个答案也非常简洁。
猜你喜欢
  • 2021-11-21
  • 2021-12-15
  • 2018-05-30
  • 2017-11-12
  • 2021-12-23
  • 2021-11-15
  • 2014-11-17
相关资源
最近更新 更多