如何将额外的参数传递给 Python 装饰器?

2025-03-18 08:54:00
admin
原创
39
摘要:问题描述:我有一个像下面这样的装饰器。def myDecorator(test_func): return callSomeWrapper(test_func) def callSomeWrapper(test_func): return test_func @myDecorator de...

问题描述:

我有一个像下面这样的装饰器。

def myDecorator(test_func):
    return callSomeWrapper(test_func)

def callSomeWrapper(test_func):
    return test_func

@myDecorator
def someFunc():
    print 'hello'

我想增强这个装饰器以接受如下所示的另一个参数

def myDecorator(test_func,logIt):
    if logIt:
        print "Calling Function: " + test_func.__name__
    return callSomeWrapper(test_func)

@myDecorator(False)
def someFunc():
    print 'Hello'

但是这段代码给出了错误,

TypeError:myDecorator() 需要 2 个参数(给出了 1 个)

为什么函数没有自动传递?如何显式地将函数传递给装饰器函数?


解决方案 1:

由于您像调用函数一样调用装饰器,因此它需要返回另一个函数,即实际的装饰器:

def my_decorator(param):
    def actual_decorator(func):
        print("Decorating function {}, with parameter {}".format(func.__name__, param))
        return function_wrapper(func)  # assume we defined a wrapper somewhere
    return actual_decorator

外部函数将获得您明确传递的任何参数,并应返回内部函数。内部函数将传递要修饰的函数,并返回修改后的函数。

通常,您希望装饰器通过将函数包装在包装函数中来更改函数行为。以下是可选地在调用函数时添加日志记录的示例:

def log_decorator(log_enabled):
    def actual_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if log_enabled:
                print("Calling Function: " + func.__name__)
            return func(*args, **kwargs)
        return wrapper
    return actual_decorator

functools.wraps调用将名称和文档字符串等内容复制到包装函数,以使其更类似于原始函数。

使用示例:

>>> @log_decorator(True)
... def f(x):
...     return x+1
...
>>> f(4)
Calling Function: f
5

解决方案 2:

只是为了提供不同的观点:语法

@expr
def func(...): #stuff

相当于

def func(...): #stuff
func = expr(func)

具体来说,expr可以是任何你喜欢的,只要它求值为可调用。具体来说expr可以是装饰器工厂:你给它一些参数,它就会给你一个装饰器。所以也许理解你的情况的更好方法是

dec = decorator_factory(*args)
@dec
def func(...):

然后可以缩短为

@decorator_factory(*args)
def func(...):

当然,由于它看起来像是decorator_factory一个装饰器,人们倾向于用这个名字来反映它。当你试图遵循间接层级时,这可能会令人困惑。

解决方案 3:

这只是另一种装饰器使用方法。我发现这种方法最容易理解。

class NiceDecorator:
    def __init__(self, param_foo='a', param_bar='b'):
        self.param_foo = param_foo
        self.param_bar = param_bar

    def __call__(self, func):
        def my_logic(*args, **kwargs):
            # whatever logic your decorator is supposed to implement goes in here
            print('pre action baz')
            print(self.param_bar)
            # including the call to the decorated function (if you want to do that)
            result = func(*args, **kwargs)
            print('post action beep')
            return result

        return my_logic

# usage example from here on
@NiceDecorator(param_bar='baaar')
def example():
    print('example yay')


example()

解决方案 4:

只想添加一些有用的技巧,使装饰器参数成为可选的。它还允许重用装饰器并减少嵌套

import functools

def myDecorator(test_func=None,logIt=None):
    if test_func is None:
        return functools.partial(myDecorator, logIt=logIt)
    @functools.wraps(test_func)
    def f(*args, **kwargs):
        if logIt==1:
            print 'Logging level 1 for {}'.format(test_func.__name__)
        if logIt==2:
            print 'Logging level 2 for {}'.format(test_func.__name__)
        return test_func(*args, **kwargs)
    return f

#new decorator 
myDecorator_2 = myDecorator(logIt=2)

@myDecorator(logIt=2)
def pow2(i):
    return i**2

@myDecorator
def pow3(i):
    return i**3

@myDecorator_2
def pow4(i):
    return i**4

print pow2(2)
print pow3(2)
print pow4(2)

解决方案 5:

现在如果你想调用一个function1带有装饰器的函数decorator_with_arg,并且在这种情况下函数和装饰器都接受参数,

def function1(a, b):
    print (a, b)

decorator_with_arg(10)(function1)(1, 2)

解决方案 6:

接受多个输入的装饰器就像dataclasses装饰器

在该示例中,dataclass接受三种语法:

@dataclass
class C:
    ...

@dataclass()
class C:
    ...

@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False,
           match_args=True, kw_only=False, slots=False, weakref_slot=False)
class C:
    ...

在创建具有相同行为的装饰器时,您可以使用它,

import inspect

def customdecorator(*args, **kwargs):
    def decorator(func):
        print('input decorator:', args, kwargs)
        def __wrapper(*func_args, **func_kwargs):
            print('input decorator inside function:', args, kwargs)
            print('input function:', func_args, func_kwargs)
            # Do something before calling the function
            result = func(*func_args, **func_kwargs)
            # Do something after calling the function
            return result
        return __wrapper

    print('input root:', args, kwargs)
    if len(kwargs) > 0:
        # Decorator is used with arguments, e.g., @functionmethod(arg1=val1, arg2=val2)
        return decorator
    if len(args) == 0:
        return decorator
    if len(args) == 1:
        return decorator(args[0])

# Example usages
@customdecorator
def example1():
    print("Function without call")

@customdecorator()
def example2():
    print("Function without arguments")

@customdecorator(arg1="value1", arg2="value2")
def example3(arg2):
    print(f"Function with arguments: {arg2}")

example1()
example2()
example3(arg2="ex2")

就你的情况而言,

def myDecorator(*args, **kwargs):
    def decorator(func):
        def __wrapper(*func_args, **func_kwargs):
            # Do something before calling the function
            test_func = kwargs.get('test_func', None)
            logIt = kwargs.get('logIt', None)
            if logIt:
                print("Calling Function: " + test_func.__name__)

            result = func(*func_args, **func_kwargs)

            # Do something after calling the function
            return result
        return __wrapper

    if len(kwargs) > 0:
        # Decorator is used with arguments, e.g., @functionmethod(arg1=val1, arg2=val2)
        return decorator
    if len(args) == 0:
        return decorator
    if len(args) == 1:
        return decorator(args[0])


@myDecorator(logIt=False)
def someFunc():
    print('Hello')

someFunc()

需要注意的是:

  1. 可选参数必须使用键参数。

  2. 默认值通过 输入kwargs.get(..., ...)

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用