Подтвердить что ты не робот

Интроспекция Python: получить список аргументов метода method_descriptor?

Обозначение кода как введение в мои вопросы:

import re, inspect, datetime

inspect.getargspec (re.findall)
# =>
# ArgSpec(args = ['pattern', 'string', 'flags'], varargs=None,
# keywords=None, defaults = (0,))

type (datetime.datetime.replace)
# => <type 'method_descriptor'>

inspect.getargspec (datetime.datetime.replace)
# => Traceback (most recent call last):
#      File "<stdin>", line 1, in <module>
#      File "/usr/lib/python2.7/inspect.py", line 816, in getargspec
#        raise TypeError('{!r} is not a Python function'.format(func))
# TypeError: <method 'replace' of 'datetime.datetime' objects> is
# not a Python function

Кажется, что единственный способ найти подпись datetime.datetime.replace, в то время как я код, это посмотреть в документ: date.replace(year, month, day).

Единственная часть интроспекции, которая, кажется, работает:

datetime.datetime.replace.__doc__
# => 'Return datetime with new specified fields.'

Я изучил, как работает инструмент-подсказка аргумента функции Jupyter, у них есть одна и та же проблема, т.е. для datetime.datetime.replace нет аргумента.

Итак, вот вопросы:

  • Можно ли каким-либо образом получить список аргументов? Может быть, я смогу установить источники C для datetime и подключить их через атрибут __file__?

  • Можно ли аннотировать a <type 'method_descriptor'> с информацией о arglist? В этом случае я мог бы проанализировать определение уценки связанного документа и автоматически аннотировать встроенные функции модуля.

4b9b3361

Ответ 1

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

if (! PyArg_ParseTupleAndKeywords(args, kw, "|iiiiiiiO$i:replace",
                                  datetime_kws,
                                  &y, &m, &d, &hh, &mm, &ss, &us,
                                  &tzinfo, &fold))

Функция re.findall() представляет собой чистую функцию Python, так что это интроспективно.

Я сказал большинство методов, определенных в C, потому что с Python 3.4 и выше методы, которые используют новый препроцессор "Аргумент клиники" , будут включать новый атрибут __text_signature__, который может анализировать внутренняя функция inspect._signature_fromstr(). Это означает, что даже для таких C-определенных методов вы можете проанализировать аргументы:

>>> import io
>>> import inspect
>>> type(io.BytesIO.read)
<class 'method_descriptor'>
>>> inspect.signature(io.BytesIO.read)
<Signature (self, size=None, /)>

Также см. Что такое __signature__ и __text_signature__, используемые в Python 3.4

Модуль datetime еще не получил много любви к Аргументской клинике. Мы должны будем проявлять терпение, или если вам действительно очень понравилось, поставьте патчи, которые преобразуют модуль в клинику Аргумент.

Если вы хотите узнать, какие модули уже имеют поддержку, просмотрите подкаталог Modules/clinic, который содержит сгенерированный выход клиники; для модуля datetime в настоящее время включена только datetime.datetime.now(). Этот метод определяет блок клиники:

/*[clinic input]
@classmethod
datetime.datetime.now
    tz: object = None
        Timezone object.
Returns new datetime object representing current time local to tz.
If no tz is specified, uses local timezone.
[clinic start generated code]*/

static PyObject *
datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz)
/*[clinic end generated code: output=b3386e5345e2b47a input=80d09869c5267d00]*/

делает метод интроспективным:

>>> import datetime
>>> inspect.signature(datetime.datetime.now)
<Signature (tz=None)>

Невозможно напрямую привязать информацию к этим C-функциям и методам, которые не являются интроспективными; они также не поддерживают атрибуты.

Большинство автозаполненных решений, которые хотят поддерживать такие объекты, используют отдельные структуры данных, где информация поддерживается независимо (со всеми присущими рисками из-за синхронизации данных). Некоторые из них доступны для ваших целей:

  • Интеллектуальная библиотека кода Комодо (open source, также используемые другими редакторами) использует формат CIX для кодирования этих данных; вы можете загрузить каталог Python 3. К сожалению, для вашего конкретного примера сигнатура функции datetime.replace() также не была отображена:

    <scope doc="Return datetime with new specified fields." ilk="function" name="replace" />
    
  • Новый синтаксис намека на тип Python 3.5 также должен знать, какие объекты объектов ожидать, и для этого необходимо, чтобы файлы-заглушки были предоставлены для объектов, которые нельзя интродуцировать. Этот проект на основе питонов Python. Сюда входят все имена аргументов для datetime module:

    class datetime:
        # ...
        def replace(self, year: int = ..., month: int = ..., day: int = ..., hour: int = ...,
            minute: int = ..., second: int = ..., microsecond: int = ..., tzinfo:
            Optional[_tzinfo] = None) -> datetime: ...
    

    Вам придется самому разбирать такой файл; они не всегда могут быть импортированы в качестве ссылочных типов заглушек, которые еще не определены, вместо использования пересылаемых ссылок:

    >>> import importlib.machinery
    >>> path = 'stdlib/3/datetime.pyi'
    >>> loader = importlib.machinery.SourceFileLoader('datetime', path)
    >>> loader.load_module()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<frozen importlib._bootstrap_external>", line 399, in _check_name_wrapper
      File "<frozen importlib._bootstrap_external>", line 823, in load_module
      File "<frozen importlib._bootstrap_external>", line 682, in load_module
      File "<frozen importlib._bootstrap>", line 251, in _load_module_shim
      File "<frozen importlib._bootstrap>", line 675, in _load
      File "<frozen importlib._bootstrap>", line 655, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 678, in exec_module
      File "<frozen importlib._bootstrap>", line 205, in _call_with_frames_removed
      File "stdlib/3/datetime.pyi", line 12, in <module>
        class tzinfo:
      File "stdlib/3/datetime.pyi", line 13, in tzinfo
        def tzname(self, dt: Optional[datetime]) -> str: ...
    NameError: name 'datetime' is not defined
    

    Возможно, вы сможете обойти это, используя предварительно определенный объект модуля и глобальные переменные, а затем итерацию ошибок имени до тех пор, пока он не импортирует. Я оставлю это как упражнение для читателя. Mypy и другие контролеры типов не пытаются выполнить код, они просто строят AST.

Ответ 2

Проблема, с которой вы сталкиваетесь, вызвана тем, что C-кодированные функции не выставляют свою подпись. Вы найдете более подробную информацию об этом ответе на "Как узнать арность метода в Python" .

В вашем случае re.findall определяется в Python (см. def findall(pattern, string, flags=0):), а datetime.datetime.replace записывается в C (см. datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)).

Вы можете видеть, как использовать различные доступные атрибуты (и в частности атрибут __code__) в функции с dir builtin:

>>> dir(datetime.datetime.replace)
['__call__', '__class__', '__delattr__', '__doc__', '__format__', '__get__', '__getattribute__', '__hash__', '__init__', '__name__', '__new__', '__objclass__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> dir(re.findall)
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
>>> datetime.datetime.replace.__code__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'method_descriptor' object has no attribute '__code__'
>>> re.findall.__code__
<code object findall at 0x7fe7234e74b0, file "/usr/lib/python2.7/re.py", line 173>

Обычно help дает вам то, что вам нужно (на основе атрибута __doc__), но в вашем случае это, похоже, мало помогает:

>>> help(datetime.datetime.replace)
Help on method_descriptor:

replace(...)
    Return datetime with new specified fields.

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