【问题标题】:How to remove the self argument from a decorator's arguments if there is one如果有一个装饰器的参数,如何从装饰器的参数中删除 self 参数
【发布时间】:2021-09-17 13:14:58
【问题描述】:

假设我有一个简单的装饰器,以及一个类方法和一个函数,我都用这个装饰器装饰:

import functools

def decorator(func):
    @functools.wraps(func)
    def call(*args):
        print(args)
        func(*args)
    return call

class cls:
    @decorator
    def meth(self, a):
        pass

@decorator
def func(c):
    pass

cls().meth("a")
func("c")

我得到以下输出:

(<__main__.cls object at 0x7f4665c50130>, 'a')
('c',)

但是我想在方法上使用装饰器时去掉self参数,这样输出就变成了:

('a',)
('c',)

但是,如果我只是添加args.pop(0),我将删除第一个参数,即使它不是self。如果它是self,我怎样才能只删除第一个参数?

注意:我使用inspect 阅读了一些带有长代码的解决方案 - 但在这种伟大且易于使用的编程语言中必须有更短、更简单的方法?

编辑:使用@staticmethod 对我来说不是一个选项,因为我需要方法本身中的self 参数。我只是不希望它被打印

【问题讨论】:

  • @staticmethod 装饰器呢?
  • @May.D 不,这不是我的选择;另外我没有看到它的好处 - 我编辑了我的问题。

标签: python python-3.x object decorator self


【解决方案1】:

实现这一点的最简单方法是明确告诉您的装饰器它正在装饰一个类方法。您的装饰器将采用一个关键字参数来表示这一点。

def decorator(is_classmethod=False):
    def wrapper(func):
        @functools.wraps(func)
        def call(*args):
            if is_classmethod:
                print(args[1:])  # omit args[0]
            else:
                print(args)
            return func(*args)      # pass all the args to the function
        return call
    return wrapper

class cls:
    @decorator(is_classmethod=True)
    def meth(self, a):
        pass

@decorator
def func(c):
    pass

cls().meth("a")
func("c")

由于用户已经知道他们在装饰什么,您可以避免装饰器中复杂的自省代码,并让用户可以灵活地处理或不处理第一个参数。

请看看我对这个 SO 问题的回答:Decorators with parameters? 以更简洁(恕我直言)的方式来编写带参数的装饰器。那篇文章中有一些非常好的答案。

【讨论】:

  • 嗯......如果我没有得到另一个自动检测到的解决方案,我明天会接受这个答案! +1
  • func("c") 不打印
  • 您忘记使用is_classmethod 来更改call 的行为。
  • 您也可以将is_classmethod 替换为直接用作切片起始值的整数参数。
  • 用分支逻辑更新了代码示例。 @chepner,感谢您的评论。
【解决方案2】:

您可以通过一些 python 魔法来过滤args 以获取输出。例如:

import functools

def decorator(func):
    @functools.wraps(func)
    def call(*args):
        print(list(filter(lambda x: not hasattr(x, '__dict__'), args)))
        func(*args)
    return call

class cls:
    @decorator
    def meth(self, a):
        pass

@decorator
def func(c):
    pass

cls().meth("a")  # ['a']
func("c")  # ['c']

【讨论】:

  • 如果您将任何可变类作为方法参数传递,这将不起作用。 def meth(self, other_class_instance) 将被过滤器捕获。
  • 请写一个这样的简单例子。我认为我们可以找到解决方案)
  • 据我了解,作者需要查看输入的变量,没有其他类信息。
  • OP 希望避免打印 self 引用(如果函数是类或实例方法,这是函数调用的第一个参数),并打印所有其他参数。如果任何参数结果是实现__dict__ 属性的对象,它将被您的装饰器过滤
  • 如果我将print(hasattr(self, '__dict__')) 放入cls.meth,我得到True。那么filter(lambda x: not hasattr(x, '__dict__'), args) 是如何工作的呢?请解释一下。
猜你喜欢
  • 2019-02-11
  • 2014-07-21
  • 2012-12-05
  • 1970-01-01
  • 2020-02-06
  • 2011-08-02
  • 2022-08-15
  • 2018-01-07
相关资源
最近更新 更多