私は最近Pythonで遊んでいますが、少し奇妙に感じていることの1つは、長さを利用可能にするため、オブジェクトがメソッド、def __len__(self)
、そしてlen(obj)
を書くときに呼び出されます。
オブジェクトがlen(self)
メソッドを単純に定義せず、オブジェクトのメンバーとして直接呼び出すのはなぜだろうと思っていました。 obj.len()
? Pythonのようにそれを行うのには十分な理由があるに違いないが、初心者として、私は彼らがまだ何をしていないかを確かめていない。
知る限り、len
はこの点で特別であり、歴史的なルーツを持っています。
引用 FAQから :
なぜPython一部の機能(list.index()など)のメソッドを使用するが、他の機能(たとえばlen(list))を使用するのか?
主な理由は歴史です。関数は、タイプのグループに対して汎用的であり、メソッドをまったく持たないオブジェクト(タプルなど)でも機能することを目的とした操作に使用されました。 Python(map()、apply()et al)。の機能的特徴を使用するとき、オブジェクトの不定形のコレクションに容易に適用できる関数を持つことも便利です。
実際、len()、max()、min()を組み込み関数として実装すると、実際には各タイプのメソッドとして実装するよりもコードが少なくなります。個々のケースについて口論することはできますが、それはPythonの一部であり、今このような根本的な変更を行うには遅すぎます。大規模なコード破損を回避するために、関数は残っている必要があります。
他の「魔法のメソッド」(Python folklore)で実際にspecial methodと呼ばれる)は多くの意味を持ち、同様の機能は他の言語にも存在します。それらは主に、特別な構文が使用されたときに暗黙的に呼び出されるコードに使用されます。
例えば:
等々...
PythonのZenから:
あいまいさに直面して、推測する誘惑を拒否します。
それを行うための明白な方法は、1つ(できれば1つだけ)でなければなりません。
これが理由の1つです。カスタムメソッドを使用すると、開発者はgetLength()
、length()
、getlength()
などのような別のメソッド名を自由に選択できます。 Pythonは、共通関数len()
を使用できるように厳密な命名を強制します。
多くのタイプのオブジェクトに共通するすべての操作は、__nonzero__
、__len__
、または__repr__
などのマジックメソッドに入れられます。ただし、ほとんどはオプションです。
演算子のオーバーロードは、マジックメソッド(たとえば、__le__
)でも実行されるため、他の一般的な操作にも使用するのが理にかなっています。
PythonはWord "magic methods"を使用します。これらのメソッドは実際にプログラムに対して魔法を実行するからです。 Pythonの魔法のメソッドを使用する最大の利点の1つは、オブジェクトを組み込み型のように動作させる簡単な方法を提供することです。つまり、基本的な演算子を実行するofい、直感に反する、非標準的な方法を避けることができます。
次の例を考えてみましょう。
dict1 = {1 : "ABC"}
dict2 = {2 : "EFG"}
dict1 + dict2
Traceback (most recent call last):
File "python", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
辞書タイプは追加をサポートしていないため、これはエラーになります。それでは、辞書クラスを拡張して、 "__ add __"マジックメソッドを追加しましょう。
class AddableDict(dict):
def __add__(self, otherObj):
self.update(otherObj)
return AddableDict(self)
dict1 = AddableDict({1 : "ABC"})
dict2 = AddableDict({2 : "EFG"})
print (dict1 + dict2)
現在、次の出力が得られます。
{1: 'ABC', 2: 'EFG'}
したがって、このメソッドを追加することにより、突然魔法が発生し、以前に取得していたエラーはなくなりました。
私はあなたに物事を明確にすることを願っています。詳細については、次を参照してください。
Pythonのマジックメソッドのガイド(Rafe Kettler、2012)
これらの関数のいくつかは、単一のメソッドで実装できる以上のことを行います(スーパークラスの抽象メソッドなし)。たとえば、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
、setattr
およびdelattr
)。 int
のようなものも、強制を行うときにマジックメソッドにアクセスします(___int__
_を実装できます)が、型として2つの役割を果たします。 len(obj)
は、実際にobj.__len__()
と異なるとは思わない1つのケースです。
それらは実際には「魔法の名前」ではありません。これは、オブジェクトが特定のサービスを提供するために実装しなければならないインターフェースです。この意味で、これらは再実装する必要がある定義済みのインターフェース定義ほど魔法ではありません。
その理由はほとんど歴史的なものですが、Pythonのlen
には、適切なメソッドではなく関数を使用するという特殊性がいくつかあります。
Python=の一部の操作はlist.index
やdict.append
などのメソッドとして実装されていますが、他の呼び出しはstr
やiter
やreversed
などの呼び出し可能メソッドやマジックメソッドとして実装されています。アプローチは正当化されます:
str
、int
および友人はタイプです。コンストラクターを呼び出す方が理にかなっています。__getitem__
が使用できない場合、iter
は__iter__
を呼び出し、メソッド呼び出しに適合しない追加の引数をサポートします。同じ理由で、最近のバージョンのPython-より意味があります]でit.next()
がnext(it)
に変更されました。__iter__
および__next__
を呼び出すための構文があります-for
ループと呼ばれます。一貫性を保つには、関数の方が優れています。そして、それは特定の最適化のためにそれを改善します。repr
はstr
のように動作します。 str(x)
とx.repr()
があるのは混乱するでしょう。isinstance
など、実際の実装方法をほとんど使用しないものもあります。getattr(x, 'a')
はx.a
を行う別の方法であり、getattr
は前述の品質の多くを共有します。私は個人的に、最初のグループをメソッドのように、2番目のグループを演算子のように呼び出します。これはあまり良い区別ではありませんが、何らかの形で役立つことを願っています。
そうは言っても、len
は2番目のグループに完全には適合しません。最初の操作よりも操作に近いですが、ほとんどの操作よりも一般的であるという唯一の違いがあります。しかし、それが行う唯一のことは__len__
を呼び出すことであり、L.index
に非常に近いです。ただし、いくつかの違いがあります。たとえば、bool
などの他の機能の実装のために__len__
が呼び出される場合があります。メソッドがlen
と呼ばれる場合、まったく異なることを行うカスタムlen
メソッドでbool(x)
を壊す可能性があります。
要するに、クラスが実装する可能性のある非常に一般的な機能のセットがあり、それは演算子、特別な関数(通常は演算子のように実装以上のことを行う)、オブジェクト構築中、およびそれらすべてにアクセスできますいくつかの共通の特徴を共有します。残りはすべてメソッドです。また、len
はそのルールの例外です。
上記の2つの投稿に追加することは多くありませんが、すべての「マジック」機能はまったく魔法ではありません。これらは、インタプリタの起動時に暗黙的/自動的にインポートされる__ builtins__モジュールの一部です。つまり:
from __builtins__ import *
プログラムが開始する前に毎回発生します。
Pythonが対話型シェルに対してのみこれを行い、必要なビルトインからさまざまなパーツをインポートするために必要なスクリプトである場合。シェルvsインタラクティブ。とにかく、すべての機能をチェックして、それらがない場合の機能を確認してください。
dir (__builtins__)
...
del __builtins__