5

長期保存用のh5pyモジュールを使用して、深くネストされたクラス、属性、バインドされたメソッドなどのセットをHDF5ファイルに書き込もうとしています。私は本当に近いです。私が解決できないと思われる唯一の問題は、プログラムで、実行時に、何かがリストや整数などではなく、クラスインスタンスタイプであるかどうかを判断する方法を見つけることです。クラスインスタンスですが、明らかにintやfloatなどに再帰するべきではありません。これは古いスタイルと新しいスタイルの両方のクラスで機能する必要があります。私が調べたものでうまくいかない/仕事に就けない:

検査モジュールの使用

>>> class R(object): pass
...
>>> _R = R()
>>> import inspect
>>> inspect.isclass(_R)
False
>>> inspect.isclass(R)
True

これは役に立ちません、私はinspect.isclassinstance(_R)戻るような関数が必要ですTrue

タイプモジュールの使用

古いスタイルのクラスを使用する場合、以下のコードのように、古いスタイルのクラスのインスタンスと一致するInstanceTypeというタイプがあります。

>>> import types
>>> class R(): pass #old-style class
...
>>> _R = R()
>>> import types
>>> type(_R) is types.InstanceType
True
>>> class R(object): pass #new-style class
...
>>> _R = R()
>>> type(_R) is types.InstanceType
False

ただし、新しいスタイルのクラスを使用する場合、対応するタイプはありません。types

4

3 に答える 3

6

classポスターはおそらく彼のデザインを再考する必要があるかもしれませんが、場合によっては、Cで作成された組み込み/拡張タイプのインスタンスと、ステートメントを使用してPythonで作成されたクラスのインスタンスを区別する正当な必要があります。どちらも型ですが、後者は、型構造が実行時に割り当てられるため、CPythonが内部で「ヒープ型」と呼ぶ型のカテゴリです。そのpythonはそれらを区別し続けます出力で見ることができ__repr__ます:

>>> int       # "type"
<type 'int'>
>>> class X(object): pass
... 
>>> X         # "class"
<class '__main__.X'>

区別は、__repr__タイプがヒープタイプであるかどうかをチェックすることによって正確に実装されます。

アプリケーションの正確なニーズに応じて、is_class_instance関数は次のいずれかの方法で実装できます。

# Built-in types such as int or object do not have __dict__ by
# default. __dict__ is normally obtained by inheriting from a
# dictless type using the class statement.  Checking for the
# existence of __dict__ is an indication of a class instance.
#
# Caveat: a built-in or extension type can still request instance
# dicts using tp_dictoffset, and a class can suppress it with
# __slots__.
def is_class_instance(o):
    return hasattr(o, '__dict__')

# A reliable approach, but one that is also more dependent
# on the CPython implementation.
Py_TPFLAGS_HEAPTYPE = (1<<9)       # Include/object.h
def is_class_instance(o):
    return bool(type(o).__flags__ & Py_TPFLAGS_HEAPTYPE)

編集

関数の2番目のバージョンの説明は次のとおりです。CPythonが独自の目的で内部的に使用するのと同じテストを使用して、タイプが「ヒープタイプ」であるかどうかを実際にテストします。これにより、ヒープタイプ(「クラス」)のインスタンスでは常にTrueが返され、非ヒープタイプ(「タイプ」だけでなく、修正が容易な古いスタイルのクラス)のインスタンスではFalseが返されます。これは、経営幹部レベルの構造体のtp_flagsメンバーにビットが設定されているかどうかをチェックすることによって行われます。実装の弱点は、の値をハードコードすることです。PyTypeObjectPy_TPFLAGS_HEAPTYPEPy_TPFLAGS_HEAPTYPE現在観測されている値に対して一定です。(定数はシンボリック名でPythonに公開されていないため、これが必要です。)理論的にはこの定数は変更される可能性がありますが、そのような変更は既存の拡張モジュールのABIを不当に壊すため、実際には発生しない可能性が非常に高くなります。の定数定義をPy_TPFLAGS見るとInclude/object.h、古いものを乱すことなく、新しいものが注意深く追加されていることがわかります。もう1つの弱点は、このコードがJythonやIronPythonなどの非CPython実装で実行される可能性がゼロであることです。

于 2012-09-02T15:31:07.417 に答える
0

@ user4815162342のおかげで、これを機能させることができました。これは、古いスタイルと新しいスタイルのクラスのインスタンスに対してTrueを返すわずかに変更されたバージョンです。

#Added the check for old-style class
Py_TPFLAGS_HEAPTYPE = (1L<<9)       # Include/object.h
def is_class_instance(o):
    import types
    return (bool(type(o).__flags__ & Py_TPFLAGS_HEAPTYPE) 
            or type(o) is types.InstanceType)
于 2012-09-03T20:58:39.763 に答える
0

tl;dr はるか下で定義された関数を呼び出すだけです。is_object_pure_python()

ibellのように、私はuser4815162342の権威あるPython2.x固有のソリューションに忠実に感銘受けまし。しかし、Pythonicの楽園ではすべてがうまくいきません。

問題。どこでも問題。

その解決策は(洞察に満ちていますが)、次のような単純な編集では簡単に解決できないビット腐敗に苦しんでいます。

  • タイプサフィックスは、LPython3.xではサポートされていません。確かに、自明に解決可能です。
  • クロスインタープリターのis_class_instance()実装では、で最適化された純粋なPythonクラスを考慮できません__slots__
  • CPython固有のis_class_instance()実装は、CPython以外のインタープリター(pypyなど)では失敗します。
  • (クラスインスタンスではなく)クラスが純粋なPythonであるかCベースであるかを検出するための同等の実装はありません。

ソリューション!どこでもソリューション!

これらの問題を解決するために、次のPython 3.x固有のソリューションはL、 CPythonでの信頼性の高いCPython固有の実装を優先し、他のすべてのインタープリターでの信頼性の低いクロスインタープリター実装にフォールバックする__slots__ようにリファクタリングされました。クラスとクラスインスタンスの両方を検出するように一般化されています。is_class_instance()is_class_instance()

正気のために、最初にクラスインスタンスを検出しましょう:

import platform

# If the active Python interpreter is the official CPython implementation,
# prefer a more reliable CPython-specific solution guaranteed to succeed.
if platform.python_implementation() == 'CPython':
    # Magic number defined by the Python codebase at "Include/object.h".
    Py_TPFLAGS_HEAPTYPE = (1<<9)

    def is_instance_pure_python(obj: object) -> bool:
        '''
        `True` if the passed object is an instance of a pure-Python class _or_
        `False` if this object is an instance of a C-based class (either builtin
        or defined by a C extension).
        '''

        return bool(type(obj).__flags__ & Py_TPFLAGS_HEAPTYPE)

# Else, fallback to a CPython-agnostic solution typically but *NOT*
# necessarily succeeding. For all real-world objects of interest, this is
# effectively successful. Edge cases exist but are suitably rare.
else:
    def is_instance_pure_python(obj: object) -> bool:
        '''
        `True` if the passed object is an instance of a pure-Python class _or_
        `False` if this object is an instance of a C-based class (either builtin
        or defined by a C extension).
        '''

        return hasattr(obj, '__dict__') or hasattr(obj, '__slots__')

証拠はグイドのプリンにあります

ユニットテストは、不快な真実を示しています。

>>> class PurePythonWithDict(object): pass
>>> class PurePythonWithSlots(object): __slots__ = ()
>>> unslotted = PurePythonWithDict()
>>> slotted = PurePythonWithSlots()
>>> is_instance_pure_python(unslotted)
True
>>> is_instance_pure_python(slotted)
True
>>> is_instance_pure_python(3)
False
>>> is_instance_pure_python([3, 1, 4, 1, 5])
False
>>> import numpy
>>> is_instance_pure_python(numpy.array((3, 1, 4, 1, 5)))
False

これはインスタンスのないクラスに一般化されますか?

はい、しかしそうすることは簡単ではありません。(クラスインスタンスではなく)クラスが純粋なPythonであるかCベースであるかを検出するのは奇妙なことに困難です。なんで?Cベースのクラスでさえ属​​性を提供するからです。したがって、。__dict__hasattr(int, '__dict__') == True

それにもかかわらず、これがハッキーな方法である場合、ハッキーな意志があります。不明な(おそらく平凡な)理由により、dir()ビルトインはCベースのクラスに対してのみ__dict__返されたリストから属性名を削除します。したがって、クラスが純粋なPythonであるかCベースであるかをクロスインタープリターで検出することは、によって返されたリストを繰り返し検索することになります。勝利のために:dir()__dict__

import platform

# If the active Python interpreter is the official CPython interpreter,
# prefer a more reliable CPython-specific solution guaranteed to succeed.
if platform.python_implementation() == 'CPython':
    # Magic number defined by the Python codebase at "Include/object.h".
    Py_TPFLAGS_HEAPTYPE = (1<<9)

    def is_class_pure_python(cls: type) -> bool:
        '''
        `True` if the passed class is pure-Python _or_ `False` if this class
        is C-based (either builtin or defined by a C extension).
        '''

        return bool(cls.__flags__ & Py_TPFLAGS_HEAPTYPE)

# Else, fallback to a CPython-agnostic solution typically but *NOT*
# necessarily succeeding. For all real-world objects of interest, this is
# effectively successful. Edge cases exist but are suitably rare.
else:
    def is_class_pure_python(cls: type) -> bool:
        '''
        `True` if the passed class is pure-Python _or_ `False` if this class
        is C-based (either builtin or defined by a C extension).
        '''

        return '__dict__' in dir(cls) or hasattr(cls, '__slots__')

より多くの証拠。もっとプリン。

よりテスト駆動の真実性:

>>> class PurePythonWithDict(object): pass
>>> class PurePythonWithSlots(object): __slots__ = ()
>>> is_class_pure_python(PurePythonWithDict)
True
>>> is_class_pure_python(PurePythonWithSlots)
True
>>> is_class_pure_python(int)
False
>>> is_class_pure_python(list)
False
>>> import numpy
>>> is_class_pure_python(numpy.ndarray)
False

彼女が書いたのはそれだけだ

一般的に、上記で定義した低レベル関数を、すべての可能なPythonインタープリターですべての可能なタイプをサポートする2つの高レベル関数に統合しましょう。

def is_object_pure_python(obj: object) -> bool:
   '''
   `True` if the passed object is either a pure-Python class or instance of
   such a class _or_ `False` if this object is either a C-based class
   (builtin or defined by a C extension) or instance of such a class.
   '''

   if isinstance(obj, type):
       return is_class_pure_python(obj)
   else:
       return is_instance_pure_python(obj)


def is_object_c_based(obj: object) -> bool:
   '''
   `True` if the passed object is either a C-based class (builtin or
   defined by a C extension) or instance of such a class _or_ `False` if this
   object is either a pure-Python class or instance of such a class.
   '''

   return not is_object_pure_python(obj)

見よ!純粋なPython。

于 2016-12-07T08:42:42.583 に答える