问题描述
假设我有一个简单的装饰器,以及一个类方法和一个函数,我都用该装饰器装饰了它们:
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
阅读了一些带有长代码的解决方案 - 但是在这种出色且易于使用的编程语言中必须有更短、更简单的方法吗?>
EDIT:使用 @staticmethod
不是我的选择,因为我需要方法本身中的 self
参数。我只是不希望它被打印。
解决方法
实现这一点的最简单方法是明确告诉您的装饰器它正在装饰一个类方法。您的装饰器将采用一个关键字参数来表示这一点。
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? 以更简洁的(恕我直言)方式编写带参数的装饰器。那篇文章中有一些非常好的答案。
,您可以通过一些 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']