2009-04-12 3 views
31

Я создавал простую утилиту командной строки и использовал словарь как своего рода оператор case с ключевыми словами, ссылающимися на их соответствующую функцию. Все функции имеют различное количество аргументов, требуемых в настоящее время, чтобы проверить, правильно ли пользователь ввел правильное количество аргументов, необходимых для каждой функции. Я разместил требуемую сумму внутри оператора словаря в форме {Keyword:(FunctionName, AmountofArguments)}.Программно определяющее количество параметров, которые требуется функции - Python

Эта текущая настройка работает отлично, но я просто интересовался интересом самосовершенствования, если был способ определить требуемое количество аргументов в функции, и мои попытки Google вернулись до сих пор ничего ценного, но я вижу как args и kwargs могут вкрутить такую ​​команду из-за неограниченного количества аргументов, которые они позволяют.

ответ

36

inspect.getargspec():

Получить имена и значения по умолчанию аргументов функции. Возвращается кортеж из четырех вещей: (args, varargs, varkw, defaults). args - список имен аргументов (он может содержать вложенные списки). varargs и varkw - это имена аргументов * и ** или None. defaults - кортеж значений аргументов по умолчанию или None, если нет аргументов по умолчанию; если этот кортеж имеет n элементов, они соответствуют последним n элементам, перечисленным в args.

+0

Спасибо Я только что обнаружил, что так как вы отправили этот ответ. Это именно то, что мне нужно. – tomeedee

+0

Также, поскольку у меня нет ответа на редактирование вашего ответа, у вас есть опечатка в вашем ответе, это getargspec() not getargspect() – tomeedee

+0

Должен использовать inspect.getfullargspec (func) (http://docs.python.org/3.1/ library/inspect.html # inspect.getfullargspec) для Python 3.x – Casebash

14

Что вы хотите, вообще не представляется возможным, из-за использования и списков параметров kwargs, но inspect.getargspec (Python 2.x) и inspect.getfullargspec (Python 3.x) близки.

  • Python 2.x:

    >>> import inspect 
    >>> def add(a, b=0): 
    ...  return a + b 
    ... 
    >>> inspect.getargspec(add) 
    (['a', 'b'], None, None, (0,)) 
    >>> len(inspect.getargspec(add)[0]) 
    2 
    
  • Python 3.x:

    >>> import inspect 
    >>> def add(a, b=0): 
    ...  return a + b 
    ... 
    >>> inspect.getfullargspec(add) 
    FullArgSpec(args=['a', 'b'], varargs=None, varkw=None, defaults=(0,), kwonlyargs=[], kwonlydefaults=None, annotations={}) 
    >>> len(inspect.getfullargspec(add).args) 
    2 
    
+3

'inspect.getargspec (function)' - именованный кортеж, поэтому вместо 'len (inspect.getargspec (function) [0])' вы можете использовать более читаемый 'len (inspect.getargspec (function) .args)' , –

+0

'getargspec' устарел с Python 3, вместо этого используйте' подпись' или 'getfullargspec'. – Akshay

+0

@ 1 '': Это правда, начиная с версии 2.6, тогда она не была в старой версии. – Stephan202

1

сделать каждую команду класс, производный от абстрактного базы, определяющей общую структуру команды. Как можно больше, определение свойств команд должно быть помещено в переменные класса с методами, определенными в базовом классе, обрабатывающим эти данные.

Зарегистрируйте каждый из этих подклассов с заводским классом. Этот заводский класс получает список аргументов, который решает, какую команду выполнить, создав соответствующий класс подкласс команды.

Проверка аргументов обрабатывается самими подклассами команд, используя правильно определенные общие методы из базового класса команды.

Таким образом, вам не нужно многократно кодировать одни и те же вещи, и нет необходимости эмулировать оператор switch. Он также упрощает расширение и добавление команд, поскольку вы можете просто добавить и зарегистрировать новый класс. Больше ничего не изменится.

+2

+1; Вы, сэр, смешны. Учитывая, что вы написали это после того, как принятый ответ был опубликован, я предполагаю, что вы намеренно выражая очень убедительные взгляды на защитное программирование, а не пытаясь описать самый простой способ подсчета количества аргументов, которые выполняет функция. – user1612868

0

Отличный вопрос. У меня просто была проблема, что я хотел написать функцию, которая принимает аргумент обратного вызова. В зависимости от количества аргументов этого обратного вызова его нужно вызывать по-разному.

Я начал с ответа гимеля, затем расширился, чтобы иметь дело со встроенными, которые плохо реагируют с модулем 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') 

Вы можете контролировать поведение, связанное с переменной длиной аргументов *args и **kwargs с параметром ignore_varargs.

Параметр typical_argument - это kludge: Если inspect не работает, например. на вышеупомянутых встроенных, тогда мы просто пытаемся вызвать функцию с одним аргументом и посмотреть, что произойдет.

Проблема с этим подходом заключается в том, что существует множество причин для raise TypeError: используется неправильное количество аргументов или используется неправильный тип аргументов. Позволив пользователю предоставить typical_argument, я пытаюсь обойти эту проблему.

Это нехорошо. Но это может помочь людям, которые имеют один и тот же вопрос, а также сталкиваются с тем, что inspect не может проверять реализации C-кодированных функций. Может быть, у людей есть лучшее предложение?

2

Это уже был дан ответ, но без инспектировать модуля вы можете также использовать someMethod.func_code.co_argcount

3

В Python 3, используйте someMethod.__code__.co_argcount

someMethod.func_code.co_argcount больше не работает)

+0

это уже был дан 3 месяца назад. Проверьте ответы на другие вопросы перед ответом (и, конечно же, не копируйте другие ответы) –

+0

Я искал ответ на этот вопрос, и ни один из перечисленных мне не был удовлетворительным для меня. Я нашел этот ответ в другом месте и разместил его здесь, если кто-то ищет эту проблему через Google. другие ответы. Я не копировал ни одного ответа. – Bobort

+0

Посмотрите на ответ. Хосеп Валлс сделал 4-е августа: «Это уже ответили, но без модуля проверки вы также можете использовать' someMethod.func_code.co_argcount'». Довольно похоже, не так ли? возможно, именно поэтому это было отмечено для внимания модератора. –