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

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

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

Ответ 1

inspect.getargspec():

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

Ответ 2

То, что вы хотите, вообще невозможно, из-за использования 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
    

Ответ 3

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

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

Ответ 4

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

Ответ 5

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

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

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

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

Ответ 6

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

Я начал с ответа на 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 - это kludge: If inspect не работает, например. на вышеупомянутых встроенных, тогда мы просто пытаемся вызвать функцию с одним аргументом и посмотреть, что произойдет.

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

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