Получать аргументы для любого вызываемого объекта?
Я заинтересован в том, чтобы получить общий способ получения списка аргументов и ключевых слов, взятых вызываемым объектом 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
что немного сложнее.