for - Introspección de Python: obtener la lista de argumentos de un method_descriptor?
python inspect (2)
Una ilustración de código como introducción a mis preguntas:
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
Parece que la única forma en que puedo encontrar la firma de datetime.datetime.replace
mientras date.replace(year, month, day)
es buscarla en el documento : date.replace(year, month, day)
.
La única parte de la introspección que parece funcionar:
datetime.datetime.replace.__doc__
# => ''Return datetime with new specified fields.''
He examinado cómo funciona la herramienta de Jupyter, la herramienta de información sobre arglistas, tienen exactamente el mismo problema, es decir, no hay ningún arglista disponible para datetime.datetime.replace
.
Así que aquí están las preguntas:
¿Todavía es posible obtener la lista de argumentos de alguna manera? ¿Tal vez podría instalar las fuentes de C para
datetime
y conectarlas a través del atributo__file__
?¿Es posible anotar un
<type ''method_descriptor''>
con la información del arglist? En ese caso, podría analizar la definición de reducción del documento vinculado y anotar automáticamente las funciones del módulo incorporado.
El problema que está teniendo es causado por el hecho de que las funciones con código C no exponen su firma. Encontrará más información sobre esta respuesta en "Cómo averiguar la aridad de un método en Python" .
En su caso, re.findall
está definido en Python (vea def findall(pattern, string, flags=0):
mientras que datetime.datetime.replace
está escrito en C (vea datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
).
Puede ver la vista usando los diferentes atributos disponibles (y el atributo __code__
en particular) en la función con el dir
incorporado:
>>> 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>
Por lo general, la help
le brinda lo que necesita (según el atributo __doc__
), pero en su caso, no parece ayudar mucho:
>>> help(datetime.datetime.replace)
Help on method_descriptor:
replace(...)
Return datetime with new specified fields.
Además, una idea podría ser intentar establecer el atributo __code__
en algo que se corresponda con sus necesidades, pero no puede modificar mucho los tipos incorporados sin subclases .
No, no puedes obtener más información; instalar las fuentes C no le daría un fácil acceso a las mismas. Esto se debe a que la mayoría de los métodos definidos en el código C no exponen realmente esta información; Tendrías que analizar una pieza críptica de código C :
if (! PyArg_ParseTupleAndKeywords(args, kw, "|iiiiiiiO$i:replace",
datetime_kws,
&y, &m, &d, &hh, &mm, &ss, &us,
&tzinfo, &fold))
La función re.findall()
es una función pura de Python , por lo que es introspectable.
Dije que la mayoría de los métodos están definidos en C, porque a partir de Python 3.4, los métodos que usan el nuevo preprocesador de Argument Clinic incluirán un nuevo atributo __text_signature__
, que la función inspect._signature_fromstr()
interna puede analizar. Esto significa que incluso para tales métodos definidos por C, puede realizar una introspección de los argumentos:
>>> import io
>>> import inspect
>>> type(io.BytesIO.read)
<class ''method_descriptor''>
>>> inspect.signature(io.BytesIO.read)
<Signature (self, size=None, /)>
También vea ¿ Para qué se usan __signature__ y __text_signature__ en Python 3.4?
El módulo datetime
aún no ha recibido mucho amor de Argument Clinic. Tendremos que ser pacientes, o si realmente te importa mucho esto, danos parches que conviertan el módulo a usar Argument Clinic.
Si desea ver qué módulos ya tienen soporte, consulte el subdirectorio Modules/clinic
que contiene el resultado clínico generado; para el módulo datetime
, solo se incluye actualmente datetime.datetime.now()
. Ese método define un bloque clínico :
/*[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]*/
haciendo el método introspectable:
>>> import datetime
>>> inspect.signature(datetime.datetime.now)
<Signature (tz=None)>
No hay forma de adjuntar información directamente a las funciones y métodos de C que no son introspectables; Tampoco soportan atributos.
La mayoría de las soluciones de autocompletado que desean admitir tales objetos utilizan estructuras de datos separadas donde la información se mantiene de forma independiente (con todos los riesgos inherentes de que los datos se desincronicen). Algunos de estos están disponibles para sus propios fines:
La biblioteca de inteligencia de código IDE de Komodo (código abierto, también utiliza otros editores) utiliza el formato CIX para codificar estos datos; Podrías descargar el catálogo de Python 3 . Desafortunadamente para su ejemplo específico, la firma de la función
datetime.replace()
tampoco se ha desarrollado:<scope doc="Return datetime with new specified fields." ilk="function" name="replace" />
La nueva sintaxis de sugerencias de tipo de Python 3.5 también necesita saber qué tipos de argumentos esperan los objetos, y para este fin es necesario proporcionar archivos de código auxiliar para los objetos que no se pueden analizar. El proyecto tipográfico Python proporciona estos. Esto incluye todos los nombres de argumento para el módulo
datetime
:class datetime: # ... def replace(self, year: int = ..., month: int = ..., day: int = ..., hour: int = ..., minute: int = ..., second: int = ..., microsecond: int = ..., tzinfo: Optional[_tzinfo] = None) -> datetime: ...
Tendrías que analizar tal archivo tú mismo; no siempre se pueden importar como los tipos de referencia de apéndices que aún no se han definido, en lugar de usar referencias hacia adelante :
>>> 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
Es posible que pueda solucionarlo utilizando un objeto de módulo predefinido y globales, y luego iterar en los errores de nombre hasta que se importe. Dejaré eso como un ejercicio para el lector. Mypy y otros verificadores de tipos no intentan ejecutar el código, simplemente construyen un AST.