Bezdepbonusu7Программирование: статьи и ответы

Вопрос

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

Автор вопроса: tomeedeeОтветов: 6

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

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

pythonfunctionparameters

Ответы

Ответ 1 · рейтинг 42 · gimel

inspect.getargspec ():

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

Ответ 2 · рейтинг 18 · Stephan202

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

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

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

Ответ 4 · рейтинг 2 · Josep Valls

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

Ответ 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-кодированных функций. Может быть, у людей есть предложение получше?

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы