ホームページ  >  記事  >  バックエンド開発  >  Python におけるインスタンス継承関係のオーバーロードの問題に関する簡単な説明

Python におけるインスタンス継承関係のオーバーロードの問題に関する簡単な説明

不言
不言オリジナル
2018-05-04 14:21:241352ブラウズ

この記事では主に Python でのインスタンスの継承関係のオーバーロードの問題を紹介します。これには、必要な友人が参照できるように共有します

。構築メソッド isinstance(object, classinfo) は、オブジェクトが特定のクラスのインスタンスであるかどうかを判断できます。この関係は、直接的、間接的、または抽象的なものになります。
インスタンス チェックはオーバーロードが許可されています。ドキュメントのカスタマイズ インスタンスとサブクラス チェックを参照してください。 PEP 3119 の説明によると:

ここで提案されている主なメカニズムは、組み込み関数 isinstance() および issubclass() のオーバーロードを許可することです。オーバーロードは次のように機能します。呼び出し isinstance(x, C) は最初に次のことを確認します。 C. __instancecheck__ が存在し、存在する場合は、通常の実装ではなく C.__instancecheck__(x) を呼び出します。

これが意味するのは、検出のために isinstance(x, C) を呼び出すときに、最初に C が存在するかどうかを確認するということです。 __instancecheck__ が存在する場合は、C.__instancecheck__(x) を呼び出します。返される結果はインスタンス検出の結果であり、デフォルトの判定方法はありません。

このメソッドはダックタイピングをチェックするのに役立ちます。コードでテストしました。

class Sizeable(object):
  def __instancecheck__(cls, instance):
    print("__instancecheck__ call")
    return hasattr(instance, "__len__")

class B(object):
  pass

b = B()
print(isinstance(b, Sizeable)) # output:False

False のみが出力され、__instancecheck__ は呼び出されません。 これはどうなっているでしょうか。

実行中の__instancecheck__がありません

ドキュメントが明確に記述されていないことがわかります。問題を見つけるために、isinstanceのソースコードからトレースを開始します。

[abstract.c]
int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
  _Py_IDENTIFIER(__instancecheck__);
  PyObject *checker;

  /* Quick test for an exact match */
  if (Py_TYPE(inst) == (PyTypeObject *)cls)
    return 1;
  ....
}

Py_TYPE(inst) == (PyTypeObject *)cls これは、 type(inst) is cls と同等の高速マッチング メソッドであり、この高速メソッドが正常に一致した場合、 Check __instancecheck__ には進みません。したがって、C.__instancecheck__ の存在に関するドキュメント内の優先順位チェックは間違っています。引き続きソース コードを見てください:

  /* We know what type's __instancecheck__ does. */
  if (PyType_CheckExact(cls)) {
    return recursive_isinstance(inst, cls);
  }

マクロ PyType_CheckExact:

[object.h]
#define PyType_CheckExact(op) (Py_TYPE(op) == &PyType_Type)

を展開します。つまり、cls は type から直接構築されたクラスであり、その後、判定言語が確立されます。クラス宣言で指定されたメタクラスを除き、基本的には型によって直接構築されます。テストコードから判定が成立していることが分かりましたので、 recursive_isinstance と入力します。ただし、この関数には __instancecheck__ に関するコードは見つかりませんでした。 recursive_isinstance の判定ロジックは、大まかに言うと、 __mro__ の継承順序から

def recursive_isinstance(inst, cls):
  return pyType_IsSubtype(inst, cls)

def pyType_IsSubtype(a, b):
  for mro in a.__mro__:
    if mro is b:
      return True
  return False

を判定するというものです。 PyObject_IsInstance 関数に戻って下を見てください:

if (PyTuple_Check(cls)) {
  ...
}

これは、instance(x, C) の 2 番目のパラメーターがタプルであり、処理メソッドが PyObject_IsInstance(inst, item) を再帰的に呼び出すことである場合です。続きを読む:

checker = _PyObject_LookupSpecial(cls, &PyId___instancecheck__);
if (checker != NULL) {
  res = PyObject_CallFunctionObjArgs(checker, inst, NULL);
  ok = PyObject_IsTrue(res);
  return ok;
}

明らかに、ここで __instancecheck__ が取得されるのですが、この時点までのチェック プロセスを実行するには、定義されたクラスで metaclass を指定する必要があります。残っているのは、_PyObject_LookupSpecial を追跡することだけです:

[typeobject.c]
PyObject *
_PyObject_LookupSpecial(PyObject *self, _Py_Identifier *attrid)
{
  PyObject *res;

  res = _PyType_LookupId(Py_TYPE(self), attrid);
  // 有回调的话处理回调
  // ...
  return res;
}

は Py_TYPE(self) を受け取ります。これは、指定されたメタクラスで __instancecheck__ を定義する必要があることを意味します。

概要

これまで、isinstance(x, C) の動作をオーバーロードするための条件をまとめました。

x オブジェクトは C によって直接インスタンス化できません。 __instancecheck__ は、指定されたメタクラス クラスで定義されています。

  1. テストコード:

  2. class MetaSizeable(type):
      def __instancecheck__(cls, instance):
        print("__instancecheck__ call")
        return hasattr(instance, "__len__")
    
    class Sizeable(metaclass=MetaSizeable):
      pass
    
    class B(object):
      pass
    
    b = B()
    print(isinstance(b, Sizeable)) # output: False
    print(isinstance([], Sizeable)) # output: True

  3. ドキュメントは少し古い可能性があります。今回のテスト環境はPython3.6です。
  4. 関連する推奨事項:

Python 2.7 pandas の read_excel の詳細な説明


以上がPython におけるインスタンス継承関係のオーバーロードの問題に関する簡単な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。