2017-02-09 28 views
20

A碼圖作爲介紹到我的問題: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簽名,而我的代碼是看它在the docdate.replace(year, month, day)

,似乎工作

唯一的內省部分:

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

我已經檢查了Jupyter功能的arglist工具提示是如何工作的,他們有相同的問題,即沒有可用於datetime.datetime.replace arglist中。

所以這裏的問題:

  1. 是否仍然有可能以某種方式獲取參數列表?也許我可以安裝datetime的C源代碼並通過__file__屬性連接它們?

  2. 是否可以使用arglist信息註釋<type 'method_descriptor'>?在這種情況下,我可以解析鏈接文檔的降價定義並自動註釋內置的模塊功能。

回答

7

不,您無法獲得更多信息;安裝C源代碼不會讓您輕鬆訪問相同的內容。這是因爲大多數在C代碼中定義的方法實際上並沒有公開這些信息;你必須解析出一個rather cryptic piece of C code

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

re.findall()功能是pure Python function,所以是內省。

我說在C中定義方法,因爲像Python 3.4及以上,使用新Argument Clinic preprocessor將包括新的__text_signature__屬性,其中內部inspect._signature_fromstr() function可以解析方法。這意味着,即使是這些C-定義的方法,你可以反思的參數:

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

另見What are __signature__ and __text_signature__ used for in Python 3.4

datetime模塊尚未收到不少爭論診所的愛。我們必須耐心等待,或者如果您真的關心此事,請提供將模塊轉換爲使用參數診所的補丁程序。

如果你想看看哪些模塊已經支持,看看Modules/clinic subdirectory其中包含生成的診所輸出;對於datetime模塊,目前只包含datetime.datetime.now()。該方法defines a clinic block

/*[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函數和方法;他們也不支持屬性。

想要支持這些對象的大多數自動完成解決方案都使用單獨的數據結構,其中信息是獨立維護的(具有數據不同步的所有固有風險)。其中的一些可用於自己的目的:

  • 科莫多IDE代碼智能庫(開源,使用其他編輯過)使用CIX format編碼此數據;你可以download the Python 3 catalog。不幸的是,你的具體的例子,datetime.replace()函數簽名尚未充實了要麼

    <scope doc="Return datetime with new specified fields." ilk="function" name="replace" /> 
    
  • 新的Python 3.5類型提示語法也需要知道什麼類型的參數對象的期望,併爲此存根需要爲不能內省的對象提供文件。 Python typeshed project提供了這些。這包括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 
    

    您可以通過使用預先定義的模塊對象和全局來解決這一點,那麼:他們不能總是導入爲存根引用類型尚未確定,而不是使用forward references名稱錯誤,直到它導入。我將把它作爲讀者的練習。 Mypy和其他類型的跳棋不會嘗試執行代碼,他們只是建立一個AST。

3

您遇到的問題是由C代碼函數不公開其簽名造成的。你可以在這個answer to "How to find out the arity of a method in 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內置:

>>> 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__屬性的東西合作對應您的需求,但you can't tweak much on builtin types without subclassing