Получать аргументы для любого вызываемого объекта?

Я заинтересован в том, чтобы получить общий способ получения списка аргументов и ключевых слов, взятых вызываемым объектом Python. Это просто для функций с inspect.getargspec функция, например:

import inspect
from functools import partial

def foo(*args, **kwargs):
    return args, kwargs

print(inspect.getargspec(foo))
>>ArgSpec(args=[], varargs='args', keywords='kwargs', defaults=None)

def bar(*args):
    return args

print(inspect.getargspec(bar))
>>ArgSpec(args=[], varargs='args', keywords=None, defaults=None)

Однако, это терпит неудачу в случаях, таких как эти:

partial_function = partial(foo, kwarg="value")

inspect.getargspec(partial_function)
>>TypeError: <functools.partial object at 0x11748bc58> is not a Python function

class Foo(object):
    def __call__(self, *args, **kwargs):
        return args, kwargs

foo_instance = Foo()

inspect.getargspec(foo_instance)
>>TypeError: <__main__.Foo object at 0x116c13ed0> is not a Python function

inspect.getargspec(zip)
>>TypeError: <built-in function zip> is not a Python function

Обратите внимание, что есть способы получить аргументы для частичной функции и для вызываемого объекта, а именно:

inspect.getargspec(partial_function.func)
>>ArgSpec(args=[], varargs='args', keywords='kwargs', defaults=None)

inspect.getargspec(foo_instance.__call__)
>>ArgSpec(args=['self'], varargs='args', keywords='kwargs', defaults=None)

но я бы хотел более общий способ получения этих результатов, если это вообще возможно.

2 ответа

Все это может быть обработано с помощью inspect.signature вспомогательная функция, которая, как указано в ее документах:

Принимает широкий спектр вызываемых Python, от простых функций и классов до functools.partial() объекты.

Какие signature делает это берет ваш вызываемый и создает Signature объект из него:

>>> from inspect import signature
>>> s = signature(Foo())  # class as shown in your example

с аргументами теперь лежат в parameters атрибут сопоставления для Signature пример:

>>> s.parameters
mappingproxy({'args': <Parameter "*args">, 'kwargs': <Parameter "**kwargs">})

С partial объект, вы получите соответствующее представление:

>>> def foo(a, b, c): pass
>>> p = partial(foo, c = 30)
>>> signature(p).parameters
<Signature (a, b, *, c=20)>

Что касается встроенных функций, таких как zip это не всегда возможно, некоторые из этих функций не предоставляют соответствующие метаданные для создания сигнатуры PEP 362:

Некоторые функции могут не быть интроспективными в некоторых реализациях Python. Например, в CPython встроенные функции определены в C не предоставлять метаданных об их аргументах. Добавление поддержки для них выходит за рамки этого PEP.

Так что пока zip не тот тип, который предоставляет информацию о себе, другие, например, all:

>>> signature(all)
<Signature (iterable, /)>

вам, к сожалению, потребуется try-except для них больше ничего нельзя сделать.

Случаи, когда signature вспомогательные дескрипторы перечислены в разделе реализации PEP 362который представил это.

В общем, inspect.getargspec Некоторое время назад в Python 3 устарела, предлагаемый подход (если вы используете Python 3, то есть) заключается в использовании представления, предлагаемого через Signature объекты.

Если на Python 2 Я уверен, что вы не можете сделать это напрямую с getargspec, вы должны использовать getfullargspec как подсказывает ответ Анттиса.

getfullargspec также правильно обрабатывает партиалы. Ранее он устарел, но не устарел, поскольку считалось, что он весьма полезен для полиглотового кода Python 2/3 с одним исходным кодом.

Другой вариант signature что немного сложнее.

Другие вопросы по тегам