tl;dr はるか下で定義された関数を呼び出すだけです。is_object_pure_python()
ibellのように、私はuser4815162342の権威あるPython2.x固有のソリューションに忠実に感銘を受けました。しかし、Pythonicの楽園ではすべてがうまくいきません。
問題。どこでも問題。
その解決策は(洞察に満ちていますが)、次のような単純な編集では簡単に解決できないビット腐敗に苦しんでいます。
- タイプサフィックスは、
L
Python3.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。