1

これは、私が持っていた他の質問に関連しており、回答がありませんでした... libclangへのPython バインディングの内部で何が起こっているのかを理解しようとしていますが、そうするのに本当に苦労しています。

clang/cindex.py の CachedProperty クラスがどのように機能するかを理解するために、 Python と Python のdecorators両方に関する大量の記事を読みましたが、それでもすべてをまとめることはできません。descriptors

私が見た中で最も関連性の高いテキストは1 つの SO answerであり、このコード レシピは ActiveState にあります。これは少し役に立ちますが、前述したように、まだそこにはいません。

それでは、本題に移りましょう。なぜAssertionErrorCIndex を作成しようとしているのかを理解したいのです。関連するコードのみをここに投稿します (cindex.py は 3646 行の長さです..)。私のコードには、関連する行が 1 つしかありません。

index = clang.cindex.Index.create()

これは cindex.py の 2291 行を参照しており、次の結果が得られます。

return Index(conf.lib.clang_createIndex(excludeDecls, 0))

これからは、一連の関数呼び出しがありますが、その理由を説明することはできません。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>
  1. 次の呼び出し Index(conf.lib.clang_createIndex(excludeDecls, 0))CachedProperty.__get__メソッドになるのはなぜですか? はどう__init__ですか?
  2. メソッドが呼び出されない場合、__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)
  1. 値をどこCachedProperty.__get__に返す必要がありますか? CDLL.__getattr__メソッドの呼び出しはどこから来たのですか?

私にとって最も重要な部分

(Pdb) n
--Call--
> d:\project\clang\cindex.py(1970)__init__()
-> def __init__(self, obj):
(Pdb) p obj
40998256

これは、クラス Index が継承するの作成です。ClangObject

  1. しかし - __init__1 つのパラメーターでの呼び出しはどこにありますか? これはconf.lib.clang_createIndex(excludeDecls, 0)戻ってきたものですか?
  2. この番号 (40998256) はどこから来たのですか? 何度も同じ数字が出ます。私が理解している限り、それは単なる数字である必要がありますが、それがclang.cindex.LP_c_void_p objectアサーションが失敗した理由です。

要約すると、私にとって最善の方法は、ここでの関数呼び出しのステップバイステップのガイダンスです。

4

1 に答える 1

2

CachedPropertyオブジェクトは記述子オブジェクトです。このメソッドは、クラスでのみ使用可能でメソッドを持つ__get__インスタンスの属性に Python がアクセスしようとするたびに、自動的に呼び出されます。__get__

CachedPropertyデコレーターとして使用すると、それが呼び出さCachedPropertyれ、クラスの元の関数オブジェクトを置き換える のインスタンスが作成されますConfig。が呼び出されるのはその@CachedProperty行でCachedProperty.__init__あり、インスタンスは最終的にConfigクラスとしてConfig.lib. 構文を覚えておいてください

@CachedProperty
def lib(self):
    # ...

基本的に次のように実行されます

def lib(self):
    # ...
lib = CachedProperty(lib)

これにより、引数として渡されたCachedProperty()withのインスタンスが作成され、そのオブジェクトに設定されます。libwrappedConfig.lib

これはデバッガーで確認できます。あなたが調べることができる1つのステップ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__ます。そのため、以降のアクセスではその結果が検出され、その後、クラスの記述子config.libは使用されません。

メソッドは、式の次の属性__getattr__を満たすために呼び出されます。( 経由で)から動的にロードされたライブラリを返し、その特定のオブジェクト タイプは Python ctypes ライブラリによって処理されます。属性を特定の C 呼び出しに変換します。これが方法です。読み込まれた dll からの関数へのアクセスを参照してください。conf.lib.clang_createIndex(excludeDecls, 0)config.libcdll.LoadLibrary()CachedProperty.__get__()clang_createIndex

への呼び出しがconf.lib.clang_createIndex(excludeDecls, 0)完了すると、その結果のオブジェクトが実際に に渡されIndex()ます。Index()クラス自体にはメソッドがありませんが__init__、基本クラスClangObjectにはあります。

その戻り値が何であれ、整数のように見える表現をしています。ただし、ほぼ確実にint. を使用してオブジェクトのタイプを確認したり、 を使用してオブジェクトのtype()属性を確認したりできます。値を表すデータ型dir()であると確信しています(これは、メモリ内の実際の C 値をプロキシする Python オブジェクトです)。 ; それは整数として表されます:ctypes.c_void_p clang.cindex.LP_c_void_p

Cvoid *タイプを表します。値は整数で表されます。コンストラクターは、オプションの整数初期化子を受け入れます。

Python コードの残りの部分は、clangこの値を によってプロキシされた他の C 呼び出しに戻すだけconfig.libです。

于 2016-09-01T18:12:39.693 に答える