Отличный вопрос. У меня просто возникла проблема: я хотел написать функцию, которая принимает аргумент обратного вызова. В зависимости от количества аргументов этого обратного вызова его нужно вызывать по-разному.
Я начал с ответа gimel, затем расширил, чтобы иметь возможность работать со встроенными командами, которые плохо реагируют с модулем inspect
(raise TypeError
).
Итак, вот код, чтобы проверить, ожидает ли функция ровно один аргумент:
def func_has_one_arg_only(func, typical_argument=None, ignore_varargs=False):
"""True if given func expects only one argument
Example (testbench):
assert not func_has_one_arg_only(dict.__getitem__), 'builtin 2 args'
assert func_has_one_arg_only(lambda k: k), 'lambda 1 arg'
assert not func_has_one_arg_only(lambda k,x: k), 'lambda 2 args'
assert not func_has_one_arg_only(lambda *a: k), 'lambda *a'
assert not func_has_one_arg_only(lambda **a: k), 'lambda **a'
assert not func_has_one_arg_only(lambda k,**a: k), 'lambda k,**a'
assert not func_has_one_arg_only(lambda k,*a: k), 'lambda k,*a'
assert func_has_one_arg_only(lambda k: k, ignore_varargs=True), 'lambda 1 arg'
assert not func_has_one_arg_only(lambda k,x: k, ignore_varargs=True), 'lambda 2 args'
assert not func_has_one_arg_only(lambda *a: k, ignore_varargs=True), 'lambda *a'
assert not func_has_one_arg_only(lambda **a: k, ignore_varargs=True), 'lambda **a'
assert func_has_one_arg_only(lambda k,**a: k, ignore_varargs=True), 'lambda k,**a'
assert func_has_one_arg_only(lambda k,*a: k, ignore_varargs=True), 'lambda k,*a'
"""
try:
import inspect
argspec = inspect.getargspec(func)
except TypeError: # built-in c-code (e.g. dict.__getitem__)
try:
func(typical_argument)
except TypeError:
return False
else:
return True
else:
if not ignore_varargs:
if argspec.varargs or argspec.keywords:
return False
if 1 == len(argspec.args):
return True
return False
raise RuntimeError('This line should not be reached')
Вы можете управлять поведением, связанным с аргументами varargs *args
и **kwargs
с помощью параметра ignore_varargs
.
Параметр typical_argument
представляет собой кладж: если inspect
не работает, например на вышеупомянутых встроенных командах, то мы просто пытаемся вызвать функцию с одним аргументом и посмотреть, что произойдет.
Проблема с этим подходом состоит в том, что для raise TypeError
есть несколько причин: либо используется неправильное количество аргументов, либо используется неправильный тип аргументов. Разрешив пользователю предоставить typical_argument
, я пытаюсь обойти эту проблему.
Это нехорошо. Но это может помочь людям, которые задают тот же вопрос, а также сталкиваются с тем, что inspect
не может проверять реализации C-кодированных функций. Может быть, у людей есть предложение получше?
person
cfi
schedule
13.09.2012