list
これらの機能を使用するために、実際にである必要はありません。それがダックタイピングの要点です。を定義するものはすべて、、、、、およびその他のさまざまなものを自動的__getitem__(self, i)
に処理しx[i]
ます。また、定義し、、なども機能します。または、の代わりに定義することもできます。または両方。それはあなたがどれだけ-yになりたいかによります。for i in x
iter(x)
enumerate(x)
__len__(self)
len(x)
list(x)
__iter__
__getitem__
list
Pythonの特別なメソッドに関するドキュメントでは、それぞれの目的を説明し、それらを非常にうまく整理しています。
例えば:
class FakeList(object):
def __getitem__(self, i):
return -i
fl = FakeList()
print(fl[20])
for i, e in enumerate(fl):
print(i)
if e < -2: break
list
見えない。
実際にreal
リストがあり、そのデータを自分のものとして表現したい場合は、委任と継承の2つの方法があります。どちらも機能し、さまざまなケースでどちらも適切です。
オブジェクトが本当にプラスである場合は、継承を使用してください。list
基本クラスの動作を踏んでいることに気付いた場合は、とにかく委任に切り替えたいと思うかもしれませんが、少なくとも継承から始めてください。これは簡単:
class Universe(list): # don't add object also, just list
def __init__(self, securities):
super(Universe, self).__init__(iter(securities))
# don't also store `securities`--you already have `self`!
をオーバーライドすることもできます。これにより、初期化時間ではなく作成時にに入ることができますが、これは通常、では問題になりませ__new__
ん。(のような不変型の場合はより重要です。)iter(securities)
list
list
str
オブジェクトがリストではなくリストを所有しているという事実がその設計に固有である場合は、委任を使用してください。
委任する最も簡単な方法は明示的にです。を偽造するために定義するのとまったく同じメソッドを定義し、list
それらすべてを自分の所有するものに転送しlist
ます。
class Universe(object):
def __init__(self, securities):
self.securities = list(securities)
def __getitem__(self, index):
return self.securities[index] # or .__getitem__[index] if you prefer
# ... etc.
また、次の方法で委任を行うこともできます__getattr__
。
class Universe(object):
def __init__(self, securities):
self.securities = list(securities)
# no __getitem__, __len__, etc.
def __getattr__(self, name):
if name in ('__getitem__', '__len__',
# and so on
):
return getattr(self.securities, name)
raise AttributeError("'{}' object has no attribute '{}'"
.format(self.__class__.__name__), name)
のメソッドの多くはlist
新しいを返すことに注意してくださいlist
。代わりに新しいものを返すようにしたい場合はUniverse
、それらのメソッドをラップする必要があります。ただし、これらのメソッドの一部は二項演算子であることに注意してください。たとえば、が1つの場合のみ、両方がそうである場合、またはどちらかがそうである場合にのみa + b
返す必要がありますか?Universe
a
また、 aまたは単一のオブジェクト__getitem__
を返すことができ、前者を。でラップするだけなので、少し注意が必要です。これを行うには、の戻り値を確認するか、 ;のインデックスを確認します。どちらが適切かは、sをaの要素として使用できるかどうか、および抽出時にaとして処理するかaとして処理するかによって異なります。さらに、継承を使用している場合、Python 2では、非推奨のフレンドをラップする必要があります。これは、それらをサポートしているためです(ただし、要素ではなく常にサブリストを返すため、非常に簡単です)。list
Universe
isinstance(ret, list)
isinstance(index, slice)
list
Universe
list
Universe
__getslice__
list
__getslice__
これらのことを決定したら、少し面倒な場合でも、実装は簡単です。これは、トリッキーなために使用した3つのバージョンすべての例__getitem__
と、コメントで質問したものです。ラッピングに汎用ヘルパーを使用する方法を示しますが、この場合は1つのメソッドにしか必要ないため、やり過ぎかもしれません。
継承:
class Universe(list): # don't add object also, just list
@classmethod
def _wrap_if_needed(cls, value):
if isinstance(value, list):
return cls(value)
else:
return value
def __getitem__(self, index):
ret = super(Universe, self).__getitem__(index)
return _wrap_if_needed(ret)
明示的な委任:
class Universe(object):
# same _wrap_if_needed
def __getitem__(self, index):
ret = self.securities.__getitem__(index)
return self._wrap_if_needed(ret)
動的な委任:
class Universe(object):
# same _wrap_if_needed
@classmethod
def _wrap_func(cls, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
return cls._wrap_if_needed(func(*args, **kwargs))
def __getattr__(self, name):
if name in ('__getitem__'):
return self._wrap_func(getattr(self.securities, name))
elif name in ('__len__',
# and so on
):
return getattr(self.securities, name)
raise AttributeError("'{}' object has no attribute '{}'"
.format(self.__class__.__name__), name)
私が言ったように、これはこの場合、特に__getattr__
バージョンではやり過ぎかもしれません。のような1つのメソッドをオーバーライドし、他のすべてを委任したい場合は__getitem__
、いつでも__getitem__
明示的に定義し、他の__getattr__
すべてを処理させることができます。
この種のラッピングを頻繁に行う場合は、ラッパークラスを生成する関数、またはスケルトンラッパーを記述して詳細を入力できるクラスデコレータなどを作成できます。詳細はユースケースによって異なるため(すべて上で述べた問題は、いずれかの方向に進む可能性があります)、必要なことを魔法のように実行する万能のライブラリはありませんが、ActiveStateには、より完全な詳細を示すレシピがいくつかあります。標準ライブラリソースのいくつかのラッパーですらあります。