Python内省:获取method_descriptor的参数列表?

abo*_*abo 21 python introspection

作为我的问题介绍的代码插图:

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
Run Code Online (Sandbox Code Playgroud)

看来,对我来说,只有这样,才能找到的签名datetime.datetime.replace,而我的代码是看它在商务部:date.replace(year, month, day).

唯一可行的内省部分:

datetime.datetime.replace.__doc__
# => 'Return datetime with new specified fields.'
Run Code Online (Sandbox Code Playgroud)

我已经研究了Jupyter函数arglist工具提示是如何工作的,它们有完全相同的问题,即没有可用的arglist datetime.datetime.replace.

所以这里是问题:

  1. 是否仍然可以以某种方式得到参数列表?也许我可以安装C源datetime并通过__file__属性连接它们?

  2. 是否可以<type 'method_descriptor'>使用arglist信息注释a ?在这种情况下,我可以解析链接文档的markdown定义并自动注释内置模块函数.

Mar*_*ers 7

不,你无法获得更多信息; 安装C源代码不会让您轻松访问.那是因为C代码中定义的大多数方法实际上并没有公开这些信息; 你必须解析一段相当神秘的C代码:

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

re.findall()函数是纯Python函数,因此是内省的.

我说过在C中定义的大多数方法,因为从Python 3.4及更高版本开始,使用新的Argument Clinic预处理器的方法将包含一个新__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, /)>
Run Code Online (Sandbox Code Playgroud)

另请参阅Python 3.4中使用的__signature__和__text_signature__

datetime模块尚未收到太多Argument Clinic的爱.我们必须要有耐心,或者如果您真的非常关心这一点,请提供将模块转换为使用Argument Clinic的补丁.

如果你想看到什么模块具有支持已经,看看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]*/
Run Code Online (Sandbox Code Playgroud)

使方法内省:

>>> import datetime
>>> inspect.signature(datetime.datetime.now)
<Signature (tz=None)>
Run Code Online (Sandbox Code Playgroud)

没有办法直接将信息附加到那些不可内省的C函数和方法上; 他们也不支持属性.

大多数想要支持此类对象的自动完成解决方案使用单独的数据结构,其中信息是独立维护的(数据的所有固有风险都不同步).其中一些可用于您自己的目的:

  • Komodo IDE代码智能库(开源,也使用其他编辑器)使用CIX格式对此数据进行编码; 你可以下载Python 3目录.不幸的是,你的具体的例子,datetime.replace()函数签名尚未充实了两种:

    <scope doc="Return datetime with new specified fields." ilk="function" name="replace" />
    
    Run Code Online (Sandbox Code Playgroud)
  • 新的Python 3.5类型提示语法还需要知道对象所期望的参数类型,为此,需要为无法进行内省的对象提供存根文件.在Python的typeshed项目提供了这些.这包括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: ...
    
    Run Code Online (Sandbox Code Playgroud)

    你必须自己解析这样一个文件; 它们不能总是作为尚未定义的存根引用类型导入,而不是使用前向引用:

    >>> 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
    
    Run Code Online (Sandbox Code Playgroud)

    您可以通过使用预定义的模块对象和全局变量来解决这个问题,然后迭代名称错误,直到它导入为止.我将把它作为读者的练习.Mypy和其他类型的检查器不会尝试执行代码,它们只是构建一个AST.