Я создавал простую утилиту командной строки и использовал словарь как своего рода оператор case с ключевыми словами, связанными с их соответствующей функцией. Все функции имеют разное количество требуемых аргументов, поэтому в настоящее время, чтобы проверить, ввел ли пользователь правильное количество аргументов, необходимых для каждой функции, я поместил требуемое количество в оператор case словаря в форме {Keyword:(FunctionName, AmountofArguments)}.
Эта текущая настройка работает отлично, однако в интересах самосовершенствования мне просто было интересно, есть ли способ определить необходимое количество аргументов в функции, и мои попытки Google пока не вернули ничего ценного, но я вижу, как args и kwargs могут испортить такую команду из-за безграничного количества допустимых аргументов.
Ответ 5 · рейтинг 1 · Ber
Сделайте каждую команду классом, производным от абстрактной основы, определяющей общую структуру команды. Насколько это возможно, определение свойств команды должно быть помещено в переменные класса с методами, определенными в базовом классе, обрабатывающем эти данные.
Зарегистрируйте каждый из этих подклассов в фабричном классе. Этот фабричный класс получает список аргументов и решает, какую команду выполнять, создавая экземпляр соответствующего подкласса команд.
Проверка аргумента выполняется самими подклассами команд с использованием правильно определенных общих методов из базового класса команды.
Таким образом, вам никогда не придется многократно кодировать одно и то же, и действительно нет необходимости имитировать оператор switch. Это также упрощает расширение и добавление команд, так как вы можете просто добавить и зарегистрировать новый класс. Больше нечего менять.
Ответ 6 · cfi
Отличный вопрос. У меня просто возникла проблема: я хотел написать функцию, которая принимает аргумент обратного вызова. В зависимости от количества аргументов этого обратного вызова его нужно вызывать по-разному.
Я начал с ответа 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-кодированных функций. Может быть, у людей есть предложение получше?