一种解决方案是以某种方式检测修饰函数是否是类方法,如果不是不可能的话(据我所知),很难做到干净。这个
inspect
ismethod()
和
isfunction()
不要在类定义中使用的装饰器中工作。
考虑到这一点,这里有一种有点黑客的方法来检查修饰的callable的第一个参数是否被命名为
"self"
,这是它在类方法中的编码约定(尽管
,所以
caveat emptor
下面的代码似乎可以在python2和python3中使用。但是在python3中,它可能会
DeprecationWarning
取决于所使用的子版本,所以它们在下面的代码部分中被抑制了。
from functools import wraps
import inspect
import warnings
def validate_url(f):
@wraps(f)
def validated(*args, **kwargs):
with warnings.catch_warnings():
# Suppress DeprecationWarnings in this section.
warnings.simplefilter('ignore', category=DeprecationWarning)
# If "f"'s first argument is named "self",
# assume it's a method.
if inspect.getargspec(f).args[0] == 'self':
url = args[1]
else: # Otherwise assume "f" is a ordinary function.
url = args[0]
print('testing url: {!r}'.format(url))
assert len(url.split('.')) == 3 # Trivial "validation".
return f(*args, **kwargs)
return validated
@validate_url
def some_func(url, some_other_arg, *some_args, **some_kwargs):
print('some_func() called')
class SomeClass:
@validate_url
def some_method(self, url, some_other_args):
print('some_method() called')
if __name__ == '__main__':
print('** Testing decorated function **')
some_func('xxx.yyy.zzz', 'another arg')
print(' URL OK')
try:
some_func('https://bogus_url.com', 'another thing')
except AssertionError:
print(' INVALID URL!')
print('\n** Testing decorated method **')
instance = SomeClass()
instance.some_method('aaa.bbb.ccc', 'something else') # -> AssertionError
print(' URL OK')
try:
instance.some_method('foo.bar', 'arg 2') # -> AssertionError
except AssertionError:
print(' INVALID URL!')
** Testing decorated function **
testing url: 'xxx.yyy.zzz'
some_func() called
URL OK
testing url: 'https://bogus_url.com'
INVALID URL!
** Testing decorated method **
testing url: 'aaa.bbb.ccc'
some_method() called
URL OK
testing url: 'foo.bar'
INVALID URL!