【问题标题】:How to pass a functional argument to the decorator如何将函数参数传递给装饰器
【发布时间】:2016-02-14 22:53:00
【问题描述】:

我正在使用装饰器:deferred_set_context 将功能模块、名称和时间设置为上下文字符串(基本上需要此上下文来跟踪,以防有多次调用 register_request)

@deferred_set_context()    
def register_request(self, machine_model):

def deferred_set_context(name=None):
    def __outer__(func):
        context_name = name
        if not context_name:
            context_name = func.__name__

        @defer.inlineCallbacks
        def __inner__(*args, **kwargs):
            r = None
            with base.this_context_if_no_context(base.Context('%s.%s.%s' % (func.__module__, context_name,
                                                                         datetime.utcnow().strftime(
                                                                             '%Y-%m-%dT%H:%M:%S.%f')))):
                r = yield func(*args, **kwargs)
            defer.returnValue(r)

        return __inner__
    return __outer__

现在我需要设置将 ma​​chine_model 名称(它是 register_request 的参数)传递给这个装饰器:register_request。像这样的:

@deferred_set_context(name=machine_model)    
def register_request(self, machine_model):

我该怎么做?

【问题讨论】:

  • deferred_set_context 似乎接受了一个参数 name 并返回一个名为 __outer__ 的函数,但 name 似乎不是一个函数,因为它从未被调用,因此它不是一个装饰师。事实上,我什至不确定func 来自哪里。
  • @Tommy func 是包装函数,在本例中为 register_request

标签: python python-3.x twisted python-decorators


【解决方案1】:

注意下面两个代码sn-ps是等价的

# first
@decorator
def func():
    pass

# second
def func():
    pass
func = decorator(func)

你需要让你的装饰器接受一个函数作为参数。如果要将name作为变量传入,那么它应该是装饰器返回的函数中的参数。

【讨论】:

  • 我的印象是装饰器是一个返回函数的python函数。意思是,它必须将函数作为参数(以及返回函数)才能成为装饰器。
【解决方案2】:

假设您不想更改 deferred_set_contextregister_request 的行为,您可以创建第二个 shim 装饰器来监视正确的函数参数并将其传递给您的原始装饰器。

类似这样的:

def new_shim_decorator(register_request):
    def wrapper(self, machine_model):
        return deferred_set_context(machine_model)(self, machine_model)
    return wrapper

@new_shim_decorator
def register_request(self, machine_model):
    print("my_function called with {}".format(machine_model))

在我看来,这是一种 hacky 解决方案,因为代理原始装饰器的装饰器需要知道传递给 register_request 的参数并镜像它们。

我希望看到一个仅关键字参数,该参数可以从 shim 装饰器或原始关键字参数字典中可选地获得 get-ed:

def deferred_set_context_by_kwarg(kwarg_name):
    def outer(func):
        def wrapper(*args, **kwargs):
            return deferred_set_context(name=kwargs.get(kwarg_name))(*args, **kwargs)
        return wrapper
    return outer

@deferred_set_context_by_kwarg('machine_model')
def register_request(self, *, machine_model):
    print("my_function called with {}".format(machine_model))

注意*, machine_model* 是 Python 3 的一项功能,需要将 machine_model 指定为关键字参数。这允许我们的deferred_set_context_by_kwarg 装饰器接受一个表示我们想要的关键字参数名称的字符串,并使用该名称来检索关键字参数值并在每次函数调用时传递给deferred_set_context

【讨论】:

    【解决方案3】:

    如果您确定您的函数 register_request 始终采用相同的 2 个参数,则在装饰器代码中选择它们。

    【讨论】:

      猜你喜欢
      • 2021-11-21
      • 1970-01-01
      • 2021-08-06
      • 2020-12-18
      • 2014-11-17
      • 2021-11-15
      • 2010-12-30
      • 1970-01-01
      • 2018-08-24
      相关资源
      最近更新 更多