这与我所拥有的其他question有关,但没有答案...... 我试图了解Python binding到libclang的内幕是什么,并且真的很难这样做。
我已经在Python中阅读了关于decorators
和descriptors
的文章的TON,以了解CachedProperty class in clang/cindex.py的工作原理,但仍然无法全部这些碎片。
我见过的最相关的文字是one SO answer,以及ActiveState中的code recipe。这对我有点帮助,但是 - 正如我所提到的 - 我还没有。
所以,让我们切入追逐:
我想了解为什么我在创建CIndex时获得AssertionError
。我将在这里仅发布相关代码(cindex.py长3646行..),我希望我不会错过任何与我相关的内容。
我的代码只有一个相关的行,即:
index = clang.cindex.Index.create()
这引用line 2291 in cindex.py,产生:
return Index(conf.lib.clang_createIndex(excludeDecls, 0))
从现在开始,有一系列函数调用,我无法解释为什么和WTH来自它们。我将列出与每个部分相关的问题的代码和pdb
输出:
(重要的是要提前注意:conf.lib定义如下:)
class Config:
...snip..
@CachedProperty
def lib(self):
lib = self.get_cindex_library()
...
return lib
CachedProperty代码:
class CachedProperty(object):
"""Decorator that lazy-loads the value of a property.
The first time the property is accessed, the original property function is
executed. The value it returns is set as the new value of that instance's
property, replacing the original method.
"""
def __init__(self, wrapped):
self.wrapped = wrapped
try:
self.__doc__ = wrapped.__doc__
except:
pass
def __get__(self, instance, instance_type=None):
if instance is None:
return self
value = self.wrapped(instance)
setattr(instance, self.wrapped.__name__, value)
return value
Pdb
输出
-> return Index(conf.lib.clang_createIndex(excludeDecls, 0))
(Pdb) s
--Call--
> d:\project\clang\cindex.py(137)__get__()
-> def __get__(self, instance, instance_type=None):
(Pdb) p self
<clang.cindex.CachedProperty object at 0x00000000027982E8>
(Pdb) p self.wrapped
<function Config.lib at 0x0000000002793598>
Index(conf.lib.clang_createIndex(excludeDecls, 0))
是
CachedProperty.__get__
方法?怎么样__init__
?__init__
方法没有被调用,那么self.wrapped的方法怎么样?
值α Pdb
输出
(Pdb) r
--Return--
> d:\project\clang\cindex.py(144)__get__()-><CDLL 'libcla... at 0x27a1cc0>
-> return value
(Pdb) n
--Call--
> c:\program files\python35\lib\ctypes\__init__.py(357)__getattr__()
-> def __getattr__(self, name):
(Pdb) r
--Return--
> c:\program files\python35\lib\ctypes\__init__.py(362)__getattr__()-><_FuncPtr obj...000000296B458>
-> return func
(Pdb)
CachedProperty.__get__
应该将值返回到哪里? CDLL.__getattr__
方法的调用来自何处?最重要的部分,对我来说
(Pdb) n
--Call--
> d:\project\clang\cindex.py(1970)__init__()
-> def __init__(self, obj):
(Pdb) p obj
40998256
这是creation of ClangObject
,类索引继承自。{/ p>
__init__
调用一个参数?这是conf.lib.clang_createIndex(excludeDecls, 0)
返回的那个吗?clang.cindex.LP_c_void_p object
,这就是断言失败的原因。总结一下,对我来说最好的是对这里的函数调用的逐步指导,因为我在这一切中有点迷失......
答案 0 :(得分:2)
CachedProperty
对象是descriptor object;只要Python尝试访问仅在类上可用且具有__get__
方法的实例上的属性,就会自动调用__get__
方法。
使用CachedProperty
作为装饰器意味着调用它并创建CachedProperty
的实例来替换Config
类上的原始函数对象。 @CachedProperty
行导致CachedProperty.__init__
被调用,实例最终作为Config
传递到Config.lib
类。请记住,语法
@CachedProperty
def lib(self):
# ...
基本上以
执行def lib(self):
# ...
lib = CachedProperty(lib)
所以这会创建一个CachedProperty()
的实例,lib
作为wrapped
参数传入,然后Config.lib
设置为该对象。
你可以在调试器中看到这个;一步,你可以检查type(config).lib
:
(Pdb) type(config)
<class Config at 0x00000000027936E>
(Pdb) type(config).lib
<clang.cindex.CachedProperty object at 0x00000000027982E8>
在代码库的其余部分config
是Config
类的实例。首先,该实例在lib
对象中没有__dict__
名称,因此实例没有这样的属性:
(Pdb) 'lib' in config.__dict__
False
因此,尝试获取config.lib
必须回到类,其中Python找到Config.lib
属性,这是一个描述符对象。而不是直接使用Config.lib
,Python在这种情况下返回调用Config.lib.__get__(config, Config)
的结果。
然后__get__
方法执行原始函数(由wrapped
引用)并将其存储在config.__dict__
中。所以对<{1}}的未来访问将找到该结果,并且之后不会使用该类上的描述符。
调用config.lib
方法以满足__getattr__
表达式中的 next 属性; conf.lib.clang_createIndex(excludeDecls, 0)
从config.lib
(通过cdll.LoadLibrary()
)返回动态加载的库,specific object type由ctypes libary处理Accessing functions from loaded dlls。它会将属性转换为特定的C调用;这是CachedProperty.__get__()
方法;见Index()
class。
对clang_createIndex
的调用完成后,生成的对象确实传递给conf.lib.clang_createIndex(excludeDecls, 0)
; ClangObject
本身没有Index()
方法,但基类ctypes.c_void_p
data type有。
无论返回值是什么,它都有一个表示,看起来像一个整数。但是,它几乎肯定不是__init__
。您可以使用int
查看该对象的类型,查看它与type()
具有的属性等等。我很确定它是Observe 代表一个dir()
值(它是一个代表内存中实际C值的Python对象);它将表示为整数:
表示C
clang.cindex.LP_c_void_p
类型。该值表示为整数。构造函数接受可选的整数初始值设定项。
void *
Python代码的其余部分只会将此值传递回clang
代理的更多C调用。