上記のArmin Ronacherからの正確な説明。私のような初心者がよく理解できるように、彼の回答を拡張しています。
クラスで定義されたメソッドの違いは、静的メソッドまたはインスタンスメソッド(さらに別のタイプ-クラスメソッド-ここでは説明されていないのでスキップ)かどうかに関係なく、それらがクラスインスタンスに何らかの形でバインドされているかどうかにあります。たとえば、実行時にメソッドがクラスインスタンスへの参照を受け取るかどうかを指定します。
class C:
a = []
def foo(self):
pass
C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C()
c # this is the class instance
__dict__クラスオブジェクトのディクショナリプロパティは、クラスオブジェクトのすべてのプロパティとメソッドへの参照を保持します。
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
上記のようにメソッドfooにアクセスできます。ここで注意すべき重要な点は、Pythonのすべてがオブジェクトであるため、上記のディクショナリ内の参照自体が他のオブジェクトを指しているということです。それらをクラスプロパティオブジェクトと呼びます。簡潔にするため、私の回答の範囲内でCPOと呼んでいます。
CPOが記述子の場合、Pythonインタープリターは__get__()CPO のメソッドを呼び出して、それに含まれる値にアクセスします。
CPOが記述子であるかどうかを判断するために、Pythonインタープリターは記述子プロトコルを実装しているかどうかを確認します。記述子プロトコルを実装するには、3つのメソッドを実装します
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
例えば
>>> C.__dict__['foo'].__get__(c, C)
どこ
self CPO(list、str、functionなどのインスタンスである可能性があります)であり、ランタイムによって提供されます
instance このCPOが定義されているクラス(上記のオブジェクト 'c')のインスタンスであり、明示的に提供する必要があります
ownerこのCPOが定義されているクラス(上記のクラスオブジェクト 'C')であり、提供する必要があります。ただし、これはCPOで呼び出すためです。インスタンスでそれを呼び出すとき、ランタイムはインスタンスまたはそのクラスを提供できるため、これを提供する必要はありません(ポリモーフィズム)
value CPOの意図された値であり、当社が提供する必要があります
すべてのCPOが記述子であるとは限りません。例えば
>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510>
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'
これは、リストクラスが記述子プロトコルを実装していないためです。
したがって、c.foo(self)そのメソッドシグネチャは実際にはこれであるため、引数self in が必要です。C.__dict__['foo'].__get__(c, C)(上記で説明したように、Cは検出または多態化されるため、必要ありません)。これも、必要なインスタンス引数を渡さない場合にTypeErrorが発生する理由です。
メソッドが依然としてクラスオブジェクトCを介して参照されており、クラスインスタンスとのバインディングが、インスタンスオブジェクトの形式のコンテキストをこの関数に渡すことで実現されていることに気付いた場合。
コンテキストを保持しないか、インスタンスへのバインドを保持しないことを選択した場合、記述子CPOをラップするクラスを記述し、その__get__()メソッドをオーバーライドしてコンテキストを必要としないので、これはかなり素晴らしいです。この新しいクラスは、デコレーターと呼ばれるもので、キーワードを介して適用されます@staticmethod
class C(object):
@staticmethod
def foo():
pass
新しいラップされたCPOにコンテキストが存在しない場合foo、エラーはスローされず、次のように確認できます。
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
静的メソッドの使用例は、名前空間とコードの保守性の1つです(クラスから取り出し、モジュール全体で使用できるようにするなど)。
当然のことながら、メソッド(たとえば、インスタンス変数へのアクセス、クラス変数など)を複雑にする必要がない限り、インスタンスメソッドではなく、静的メソッドを記述する方が良いでしょう。1つの理由は、オブジェクトへの不要な参照を保持しないことにより、ガベージコレクションを容易にするためです。