Программное определение количества параметров, необходимых для функции - Python

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

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


person tomeedee    schedule 12.04.2009    source источник


Ответы (6)


inspect.getargspec ():

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

person gimel    schedule 12.04.2009
comment
Спасибо, я только что обнаружил эту функцию, когда вы опубликовали этот ответ. Это именно то, что мне нужно. - person tomeedee; 12.04.2009
comment
Кроме того, поскольку у меня нет представителя для редактирования вашего ответа, у вас есть опечатка в вашем ответе, это getargspec (), а не getargspect () - person tomeedee; 12.04.2009
comment
Следует использовать inspect.getfullargspec (func) (docs.python.org/3.1 /library/inspect.html#inspect.getfullargspec) для Python 3.x - person Casebash; 24.10.2009
comment
Еще лучше: используйте Signature объекты, представленные в Python 3.3. См. docs.python.org/3/library/inspect.html. # inspect-signature-object для документации. Они могли бы правильно избегать подсчета параметра self при работе с объектами ограниченного метода. - person Giulio Piancastelli; 21.06.2014

То, что вы хотите, в общем случае невозможно из-за использования varargs и 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
    
person Stephan202    schedule 12.04.2009
comment
inspect.getargspec(function) - это именованный кортеж, поэтому вместо len(inspect.getargspec(function)[0]) вы можете использовать более читаемый len(inspect.getargspec(function).args). - person 1''; 29.10.2014
comment
getargspec устарел с Python 3, используйте вместо него signature или getfullargspec. - person Akshay; 14.08.2016
comment
@ 1 '': это верно для версии 2.6, этого не было в старой версии, которую я использовал тогда. - person Stephan202; 22.08.2016
comment
@akshay: Спасибо за внимание. Я добавил пример Python 3.x (с использованием предложения именованного кортежа 1). - person Stephan202; 22.08.2016

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

(поскольку someMethod.func_code.co_argcount больше не работает)

person Bobort    schedule 02.11.2016
comment
на это уже был дан ответ 3 месяца назад. Проверьте остальные ответы перед тем, как отвечать (и, конечно, не копируйте другие ответы) - person Jean-François Fabre; 02.11.2016
comment
Я искал ответ на этот вопрос, и ни один из перечисленных меня не удовлетворил. Я нашел этот ответ в другом месте и разместил его здесь на случай, если кто-то придет искать эту проблему через Google. Я очень много проверял другие ответы. Я не копировал ни одного ответа. - person Bobort; 03.11.2016
comment
посмотрите ответ, который Хосеп Валлс сделал 4 августа: На этот вопрос уже был дан ответ, но без модуля проверки вы также можете использовать someMethod.func_code.co_argcount. Довольно похоже, не правда ли? возможно поэтому он был отмечен модератором. - person Jean-François Fabre; 03.11.2016
comment
Это похоже, но это НЕ то же самое. Это НЕ работает для Python 3. func_code не является атрибутом функций в Python 3. Вместо этого вы используете __code__. Более продвинутый пользователь Python мог бы разумно понять, что две версии имеют некоторые важные изменения совместимости, с которыми временами может быть неприятно иметь дело. Это одно из таких изменений. - person Bobort; 03.11.2016
comment
это правильно. Я редактирую ваш ответ, чтобы убрать голос против. - person Jean-François Fabre; 03.11.2016

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

person Josep Valls    schedule 04.08.2016

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

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

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

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

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

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

Я начал с ответа 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