2017-07-25 7 views
14

私はクラスが反復可能またはインデックス可能か、そのようなものではありませんPythonでminとmaxのカスタム値を返す方法はありますか?

class A: 
    def __init__(self, a, b): 
     self.a = a 
     self.b = b 

、カスタムクラスを持っています。可能であれば、私はそれをそのままにしたいと思います。次のようなことが可能ですか?私はこのことを考えてしまった何

>>> x = A(1, 2) 
>>> min(x) 
1 
>>> max(x) 
2 

minmaxdocsに「共通シーケンス操作」として記載されていることです。 rangeは非常に同じ文書によってシーケンス型とみなされるので、私はrangeのために可能な最適化がなければならないと考えていました。

おそらく、これを可能にする覚えがない魔法の方法がありますか?

+0

この他の投稿はあなたに役立つかもしれませんが、min/maxで動作するかどうかは完全にはわかりませんが、しかし、試してみる価値があります:https://stackoverflow.com/questions/7875911/how-to-implement-a-minimal-class-that-behaves-like-a-sequence-in-python –

+0

@depperm inこの場合、私は 'a'が' self'として渡されると信じています。 'self'を使うのは単なる慣習です。 – cssko

+1

'__iter__' /' __next__'を定義せずに動作させることを意味しますか?私はあなたの混乱を理解しているか分からない。 –

答えて

20

はい。 minが1つの引数を取るとき、それはiterableであるとみなされ、それを反復して最小値をとります。したがって、

class A: 
    def __init__(self, a, b): 
     self.a = a 
     self.b = b 
    def __iter__(self): 
     yield self.a 
     yield self.b 

追加の注意:__iter__を使用しない場合は、その方法を知らないです。独自のmin関数を作成したいと思うかもしれません。それは、渡された引数に1つがあり、それ以外の場合はminを呼び出す場合には、__min__メソッドを呼び出します。 range以来

oldmin = min 
def min(*args) 
    if len(args) == 1 and hasattr(args[0], '__min__'): 
    return args[0].__min__() 
    else: 
    return oldmin(*args) 
+6

[PEP 8はあなた自身のdunderメソッドの作成を禁止しています(https://www.python.org/dev/peps/pep-0008/)。代わりに '__min'を使用してください。 –

+1

訂正: '_min'は' __min'が名前変更されているので、 '_min'です。 –

+4

私は何を逃したのですか、下線の変種ではなく、単に「min」というメソッドを呼び出すことができませんか?それは "魔法のような"方法ではないようですが、それは単に標準ライブラリの外のどこかで呼び出されるメソッドです。 – Mephy

6

は、私はrange可能である最適化のいくつかの並べ替えがなければならないことを考えていた、おそらく私はそれを利用することができることを非常に同じドキュメントでシーケンスタイプであると考えられています。

範囲が最適化されておらず、min/maxの特殊なマジックメソッドはありません。

あなたはthe implementation for min/maxで覗いた場合には、いくつかの引数の解析が行われた後ことがわかります、a call to iter(obj)(すなわちobj.__iter__())はイテレータをつかむために行われます

it = PyObject_GetIter(v); 
if (it == NULL) { 
    return NULL; 
} 

、その後calls to next(it)(すなわちit.__next__が)で実行されていますループは、比較のための値をつかむために:

while ((item = PyIter_Next(it))) { 
    /* Find min/max */ 

それは、次の作業のようなものを持っていることは可能ですか?

いいえ、組み込みのmin *を使用する場合は、イテレータプロトコルを実装している唯一のオプションです。


* minにパッチを適用することで、あなたはのコース、それはあなたが欲しいものを行うことができます。明らかにPythonlandでの運用にかかるコストです。しかし、いくつかの最適化を利用できると思われる場合は、minを再定義するのではなく、minメソッドを作成することをお勧めします。また

、あなたが唯一のインスタンス変数としてint型を持っていて、別の電話を気にしないならば、あなたは常にinstance.__dict__を取得し、それがminから.values()だ供給するvarsを使用することができます。

あり
>>> x = A(20, 4) 
>>> min(vars(x).values()) 
4 
+0

それは意味がありますが、それは残念です。呼び出し元を特定して、 'min'または' max'以外の誰かがイテレータを取得しようとした場合に例外を発生させるように、別の質問をします。私はあなたが他の人にポイントを与える気にしないことを願っています。 –

+0

@MadPhysicist私はそれについて別の質問を投稿する必要はないと思います。コメントを読んでいる人が私と同意しない限り、次のようなことをすれば十分だと思います。https://pastebin.com/AFNGcWHw – idjaw

+1

@MadPhysicist:できません。 (あなたがしようとしているのは、巨大な "設計決定を再考する"赤旗です。) – user2357112

6

いいえ__min____max__特別な方法*です。 rangeはいくつか見たことがあるのでこれは残念です。pretty nice optimizations in Python 3

>>> 1000000000000 in range(1000000000000) 
False 

しかし、あなたが長い時間を待つようにしたい場合を除き、これを試さないでください:提案されているようしかし、あなた自身のmin/max関数を作成

>>> max(range(1000000000000)) 

は、かなり良いアイデアであるあなたはこれを行うことができますLærne

ここで私はそれをやります。 UPDATE: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を上げるためです)。

*このような方法はしばしば「魔法」と呼ばれますが、これは推奨される用語ではありません。

+5

'__min'は名前が変更されているので、その名前を付けるのは本当に面倒です。 – user2357112

+1

@ user2357112うん、それは本当です。 '_min'に変更します。 –

関連する問題