103

私は最近Pythonで遊んでいますが、少し奇妙だと思うのは、「マジックメソッド」の広範な使用です。たとえば、その長さを利用できるようにするために、オブジェクトはメソッドを実装しますdef __len__(self)。あなたが書くlen(obj)

オブジェクトが単にlen(self)メソッドを定義せず、オブジェクトのメンバーとして直接呼び出されるのはなぜだろうと思っていましたobj.len()。Pythonがそのようにそれを行うのには十分な理由があるに違いないと確信していますが、初心者として、私はまだそれらが何であるかを理解していません。

4

7 に答える 7

65

私の知る限り、lenこの点で特別であり、歴史的なルーツがあります。

FAQ からの引用は次のとおりです。

Python が一部の機能 (list.index() など) にはメソッドを使用し、他の機能 (len(list) など) には関数を使用するのはなぜですか?

大きな理由は歴史です。関数は、型のグループに対して一般的であり、メソッドをまったく持たないオブジェクト (タプルなど) に対しても機能することを意図した操作に使用されました。また、Python の関数機能 (map()、apply() など) を使用するときに、不定形のオブジェクトのコレクションに簡単に適用できる関数があると便利です。

実際、len()、max()、min() を組み込み関数として実装すると、各型のメソッドとして実装するよりもコードが少なくなります。個々のケースについて口論することはできますが、それは Python の一部であり、そのような根本的な変更を今行うには遅すぎます。大規模なコードの破損を避けるために、関数を残す必要があります。

他の「魔法のメソッド」(実際には Python の民間伝承では特殊メソッドと呼ばれています) は非常に理にかなっていて、同様の機能が他の言語にも存在します。これらは主に、特別な構文が使用されたときに暗黙的に呼び出されるコードに使用されます。

例えば:

  • オーバーロードされた演算子 (C++ などに存在)
  • コンストラクタ/デストラクタ
  • 属性にアクセスするためのフック
  • メタプログラミング用ツール

等々...

于 2010-04-17T07:32:12.053 に答える
22

Zen of Python から:

あいまいさに直面しても、推測する誘惑を断ってください。
それを行う明白な方法が 1 つ (できれば 1 つだけ) ある必要があります。

これが理由の 1 つです。カスタム メソッドを使用すると、開発者は、などgetLength()の別のメソッド名を自由に選択できます。Python では、共通の関数を使用できるように厳密な命名を強制しています。length()getlength()len()

__nonzero__多くのタイプのオブジェクトに共通するすべての操作は、__len__やなどの魔法のメソッドに入れられます__repr__。ただし、それらはほとんどオプションです。

演算子のオーバーロードもマジック メソッド ( など__le__) を使用して行われるため、他の一般的な操作にもそれらを使用することは理にかなっています。

于 2010-04-17T07:37:26.997 に答える
9

これらの関数のいくつかは、(スーパークラスの抽象メソッドなしで) 単一のメソッドが実装できる以上のことを行います。たとえばbool()、次のように動作します。

def bool(obj):
    if hasattr(obj, '__nonzero__'):
        return bool(obj.__nonzero__())
    elif hasattr(obj, '__len__'):
        if obj.__len__():
            return True
        else:
            return False
    return True

bool()また、が常に True または False を返すことを 100% 確信できます。メソッドに依存した場合、何が返されるかを完全に確信することはできませんでした。

比較的複雑な実装を持つ他のいくつかの関数 (基礎となるマジック メソッドよりも複雑である可能性が高い) はiter()、 と、およびcmp()すべての属性メソッド (getattrと) です。強制を行うときにも魔法のメソッドにアクセスするようなもの( を実装できます)が、型として2つの義務を果たします。 実際には、それが.setattrdelattrint__int__len(obj)obj.__len__()

于 2010-04-17T19:12:07.060 に答える
4

それらは実際には「魔法の名前」ではありません。これは、特定のサービスを提供するためにオブジェクトが実装する必要がある単なるインターフェイスです。この意味で、それらは、再実装しなければならない事前定義されたインターフェース定義よりも魔法ではありません。

于 2010-04-17T08:53:44.830 に答える
1

その理由は主に歴史的なものですが、Pythonlenにはメソッドの代わりに関数を適切に使用するいくつかの特殊性があります。

Python の一部の操作はメソッドとして実装されます (例:list.indexおよびdict.append)。他の操作は、呼び出し可能オブジェクトおよび魔法のメソッドとして実装されます (例:strおよびiterおよび ) reversed。2 つのグループは十分に異なるため、異なるアプローチが正当化されます。

  1. それらは一般的です。
  2. strintおよび友人はタイプです。コンストラクターを呼び出す方が理にかなっています。
  3. 実装は関数呼び出しとは異なります。たとえば、が使用できない場合にiter呼び出す可能性があり、メソッド呼び出しに収まらない追加の引数をサポートします。同じ理由で、最近のバージョンの Python ではに変更されています - より理にかなっています。__getitem____iter__it.next()next(it)
  4. これらのいくつかは、オペレーターの近親者です。呼び出すための構文があります__iter__-__next__それはforループと呼ばれます。一貫性のためには、関数の方が優れています。そして、それは特定の最適化のためにそれをより良くします。
  5. 一部の機能は、何らかの点で残りの機能にあまりにも似てreprstrます。str(x)対を持つx.repr()ことは混乱を招くでしょう。
  6. それらのいくつかは、実際の実装方法をめったに使用しませんisinstance
  7. それらの一部は実際のオペレーターでgetattr(x, 'a')あり、別の方法であり、前述の資質の多くを共有しx.aています。getattr

私は個人的に、最初のグループをメソッドのように、2 番目のグループを演算子のように呼んでいます。あまり良い区別ではありませんが、何らかの形で役立つことを願っています。

そうは言ってlenも、2番目のグループには正確には当てはまりません。最初の操作に近いですが、ほとんどの操作よりも一般的であるという唯一の違いがあります。しかし、それが行うのは を呼び出すことだけで__len__あり、 に非常に近いL.indexです。ただし、いくつかの違いがあります。たとえば__len__、 などの他の機能の実装のために が呼び出される可能性がありますbool。メソッドが呼び出された場合、まったく異なることを行うカスタムメソッドでlen壊れる可能性があります。bool(x)len

要するに、クラスが実装する可能性のある非常に一般的な機能のセットがあり、オブジェクトの構築中に、オペレーター、特別な関数 (通常、オペレーターが行うように実装以上のことを行います) を介してアクセスできます。いくつかの共通の特徴を共有します。残りはすべてメソッドです。そしてlen、その規則の例外です。

于 2011-03-05T01:37:46.560 に答える
0

上記の2つの投稿に追加することはあまりありませんが、すべての「魔法の」機能は実際にはまったく魔法ではありません。これらは__builtins__モジュールの一部であり、インタプリタの起動時に暗黙的/自動的にインポートされます。すなわち:

from __builtins__ import *

プログラムが開始する前に毎回発生します。

Pythonがインタラクティブシェルに対してのみこれを行い、必要なビルトインからさまざまなパーツをインポートするためにスクリプトが必要な場合は、より正しいと常に思っていました。また、シェルとインタラクティブでは、おそらく異なる__main__処理が適しています。とにかく、すべての関数をチェックして、それらがない場合の様子を確認してください。

dir (__builtins__)
...
del __builtins__
于 2010-04-19T22:29:53.377 に答える