カスタムクラスがあります
class A:
def __init__(self, a, b):
self.a = a
self.b = b
クラスは、反復可能でもインデックス可能でもありません。できればそのままにしていきたいと思います。次のような作品はありますか?
>>> x = A(1, 2)
>>> min(x)
1
>>> max(x)
2
min
と max
が docs 。 range
はまったく同じドキュメントでシーケンス型と見なされているため、range
で可能な最適化の種類があるはずだと考えていました。たぶん私はそれを利用できるでしょう。
おそらく私が知らない魔法の方法がこれを可能にするでしょうか?
はい。 min
が引数を1つ取るとき、それは反復可能であると想定し、それを反復処理して最小値を取ります。そう、
class A:
def __init__(self, a, b):
self.a = a
self.b = b
def __iter__(self):
yield self.a
yield self.b
うまくいくはずです。
追記:__iter__
を使用したくない場合は、その方法がわかりません。渡された引数に__min__
メソッドが存在する場合はそれを呼び出し、古いmin
を呼び出す独自のmin関数を作成する必要があるでしょう。
oldmin = min
def min(*args):
if len(args) == 1 and hasattr(args[0], '__min__'):
return args[0].__min__()
else:
return oldmin(*args)
__min__
と__max__
の特別なメソッド*はありません。 range
がいくつかの かなり良い最適化をPython で見たので、これはちょっと残念です。あなたはこれを行うことができます:
>>> 1000000000000 in range(1000000000000)
False
しかし、長い間待たない限り、これを試さないでください。
>>> max(range(1000000000000))
ただし、独自のmin
/max
関数を作成することは、 Lærne で提案されているように、かなり良いアイデアです。
ここに私がそれをする方法があります。更新: PEP 8 によって推奨されているように、__min__
を優先して、dunder名_min
を削除しました。
そのような名前を発明しないでください。文書化されているとおりにのみ使用する
コード:
from functools import wraps
oldmin = min
@wraps(oldmin)
def min(*args, **kwargs)
try:
v = oldmin(*args, **kwargs)
except Exception as err:
err = err
try:
arg, = args
v = arg._min()
except (AttributeError, ValueError):
raise err
try:
return v
except NameError:
raise ValueError('Something weird happened.')
この方法は、他の答えが考慮していないいくつかのコーナーケースを処理するため、おそらく少し優れていると思います。
_min
メソッドを含む反復可能オブジェクトは、通常どおりoldmin
によって引き続き使用されますが、戻り値は特別なメソッドによってオーバーライドされることに注意してください。
ただし、_min
メソッドでイテレーターを引き続き使用できるようにする必要がある場合は、イテレーターがoldmin
によって最初に使用されるため、これを微調整する必要があります。
__min
メソッドがoldmin
を呼び出すだけで実装されている場合も、イテレータが消費されても問題なく機能することに注意してください。これは、oldmin
がValueError
です)。
*そのような方法はしばしば「マジック」と呼ばれますが、これは好ましい用語ではありません。
range
はまったく同じドキュメントでシーケンス型と見なされているので、range
で可能な最適化がいくつかあるに違いないと考えていました。それ。
範囲の最適化は行われておらず、min
/max
のための特別な魔法のメソッドもありません。
min
/max
の実装 を覗くと、引数の解析が完了した後、 iter(obj)
(つまりobj.__iter__()
)はイテレータを取得するために作成されます:
_it = PyObject_GetIter(v);
if (it == NULL) {
return NULL;
}
_
次に next(it)
(つまり_it.__next__
_)の呼び出しがループで実行され、比較のために値を取得します。
_while (( item = PyIter_Next(it) )) {
/* Find min/max */
_
次のような作品を作ることは可能ですか?
いいえ、組み込みのmin
*を使用する場合、唯一のオプションは反復子プロトコルを実装することです。
* min
にパッチを適用することで、当然のことながら、やりたいことをすべて実行できます。明らかにPythonlandでの運用を犠牲にして。ただし、いくつかの最適化を利用できると思われる場合は、組み込みのmin
を再定義するのではなく、min
メソッドを作成することをお勧めします。
さらに、インスタンス変数としてintしかなく、別の呼び出しを気にしない場合は、常にvars
を使用して_instance.__dict__
_を取得し、それを.values()
に指定できます。 min
へ:
_>>> x = A(20, 4)
>>> min(vars(x).values())
4
_