Pythonでメンバーシップテストをエミュレートする:__contains__を含まれているオブジェクトに正しく委任する
-
06-07-2019 - |
質問
私は、Pythonが機能を他のオブジェクトに委任するための巧妙なトリックを許可していることに慣れています。 1つの例は、含まれているオブジェクトへの委任です。
しかし、__ contains __を委任したいときは、運がありません。
class A(object):
def __init__(self):
self.mydict = {}
self.__contains__ = self.mydict.__contains__
a = A()
1 in a
なる:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: argument of type 'A' is not iterable
私が間違っているのは何ですか? a .__ contains __(1)を呼び出すと、すべてがスムーズになります。 Aで__iter __メソッドを定義して、Aを反復可能に見せようとしましたが、助けにはなりませんでした。ここで何が欠けていますか?
解決
__ contains __
などの特別なメソッドは、インスタンスではなくクラスで定義されている場合にのみ特別です(Python 2のレガシークラスは例外で、とにかく使用しない) )。
したがって、クラスレベルで委任を行います。
class A(object):
def __init__(self):
self.mydict = {}
def __contains__(self, other):
return self.mydict.__contains__(other)
実際には、後者を self.mydictで他を返す
と綴りたいと思いますが、それは小さなスタイルの問題です。
編集:「インスタンスごとの特別なメソッドの完全に動的なリダイレクト」」の場合(提供される古いスタイルのクラスのように)は不可欠であり、新しいスタイルのクラスで実装するのは難しくありません。そのような特殊なニーズを持つ各インスタンスを独自の特別なクラスにラップするだけです。例:
class BlackMagic(object):
def __init__(self):
self.mydict = {}
self.__class__ = type(self.__class__.__name__, (self.__class__,), {})
self.__class__.__contains__ = self.mydict.__contains__
本質的に、ちょっとした黒魔術の後、 self .__ class __
を新しいクラスオブジェクトに再割り当てします(以前のオブジェクトと同じように動作しますが、空のdictを持ち、これ以外のインスタンスはありません self
)、 self .__ magicname __
に割り当てる古いスタイルのクラスのどこかで、代わりに self .__ class __.__ magicname __
に割り当てます(そして、それがもちろん、別の場合にインスタンスで呼び出されたときに self
を受け取りたい場合を除き、ビルトインまたは staticmethod
であり、通常のPython関数ではありません。 / p>
ちなみに、この BlackMagic
クラスのインスタンスの in
演算子は、以前に提案されたものよりも、高速です解決策-または少なくとも、通常の信頼できる -mtimeit
で測定しています(継承を含む通常のルックアップルートを追跡する代わりに、 built-in method
に直接移動し、記述子、オーバーヘッドを少し削ります。)
self .__ class __
-per-instanceのアイデアを自動化するメタクラスは書くのが難しくありません(生成されたクラスの __ new __
メソッドで汚い仕事をすることができますが、また、 __ setattr __
または多数の many プロパティを介してインスタンスに割り当てられた場合、クラスに実際に割り当てるすべてのマジックネームを設定することもできます)。しかし、この機能の必要性が本当に広まっている場合にのみ正当化されます(例:「インスタンスごとの特別なメソッド」をPython 3を含む現代のPythonに自由に使用する巨大な古代Python 1.5.2プロジェクトを移植する)。
私は推奨&quot;巧妙な&quot;または「ブラックマジック」ソリューション?いいえ、私はしません。ほとんど常に、単純で簡単な方法で物事を行う方が良いでしょう。しかし、「ほぼ」ここで重要な単語であり、このような高度な「フック」を手元に置いておくと便利です。まれではあるが、存在しないわけではない状況で、実際に使用する必要がある場合。