次のクラス メソッドの違いは何ですか?
一方は静的で、もう一方は静的ではないということですか?
class Test(object):
def method_one(self):
print "Called method_one"
def method_two():
print "Called method_two"
a_test = Test()
a_test.method_one()
a_test.method_two()
次のクラス メソッドの違いは何ですか?
一方は静的で、もう一方は静的ではないということですか?
class Test(object):
def method_one(self):
print "Called method_one"
def method_two():
print "Called method_two"
a_test = Test()
a_test.method_one()
a_test.method_two()
Python では、バインドされたメソッドとバインドされていないメソッドが区別されます。
基本的に、メンバー関数(のようなmethod_one
)、バインドされた関数への呼び出し
a_test.method_one()
に翻訳されます
Test.method_one(a_test)
つまり、バインドされていないメソッドへの呼び出しです。そのため、お使いのバージョンの への呼び出しmethod_two
は失敗し、TypeError
>>> a_test = Test()
>>> a_test.method_two()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)
デコレータを使用してメソッドの動作を変更できます
class Test(object):
def method_one(self):
print "Called method_one"
@staticmethod
def method_two():
print "Called method two"
デコレーターは、組み込みのデフォルト メタクラスtype
(クラスのクラス、この質問を参照) にバインドされたメソッドを作成しないように指示しますmethod_two
。
これで、インスタンスまたはクラスの両方で静的メソッドを直接呼び出すことができます。
>>> a_test = Test()
>>> a_test.method_one()
Called method_one
>>> a_test.method_two()
Called method_two
>>> Test.method_two()
Called method_two
記述子システムの基本を理解すれば、Python のメソッドは非常に単純なものになります。次のクラスを想像してください。
class C(object):
def foo(self):
pass
それでは、シェルでそのクラスを見てみましょう。
>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
クラスの属性にアクセスするとわかるようfoo
に、バインドされていないメソッドが返されますが、クラス ストレージ (dict) 内には関数があります。なぜですか?これは、クラスのクラスが__getattribute__
記述子を解決する を実装しているためです。複雑に聞こえますが、そうではありません。 C.foo
その特殊なケースでは、次のコードとほぼ同等です。
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
これは、関数が__get__
それらを記述子にするメソッドを持っているためです。クラスのインスタンスがある場合、それはほぼ同じで、それNone
がクラス インスタンスです。
>>> c = C()
>>> C.__dict__['foo'].__get__(c, C)
<bound method C.foo of <__main__.C object at 0x17bd4d0>>
では、なぜPythonはそれを行うのでしょうか? メソッド オブジェクトは、関数の最初のパラメーターをクラスのインスタンスにバインドするためです。そこから自我が生まれます。クラスで関数をメソッドにしたくない場合がありますstaticmethod
。
class C(object):
@staticmethod
def foo():
pass
staticmethod
デコレーターはクラスをラップし、ラップされた関数をメソッドとしてではなく関数として返すダミーを実装し__get__
ます。
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
それがそれを説明することを願っています。
クラス メンバーを呼び出すと、Python はオブジェクトへの参照を最初のパラメーターとして自動的に使用します。変数self
は実際には何も意味しません。これは単なるコーディング規則です。必要に応じて呼び出すことができますgargaloo
。そうは言っても、 への呼び出しは を発生さmethod_two
せます。これTypeError
は、Python がパラメーター (その親オブジェクトへの参照) を、パラメーターを持たないと定義されたメソッドに自動的に渡そうとするためです。
実際に機能させるには、これをクラス定義に追加できます。
method_two = staticmethod(method_two)
@staticmethod
または、関数デコレータを使用できます。
>>> class Class(object):
... def __init__(self):
... self.i = 0
... def instance_method(self):
... self.i += 1
... print self.i
... c = 0
... @classmethod
... def class_method(cls):
... cls.c += 1
... print cls.c
... @staticmethod
... def static_method(s):
... s += 1
... print s
...
>>> a = Class()
>>> a.class_method()
1
>>> Class.class_method() # The class shares this value across instances
2
>>> a.instance_method()
1
>>> Class.instance_method() # The class cannot use an instance method
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method instance_method() must be called with Class instance as first argument (got nothing instead)
>>> Class.instance_method(a)
2
>>> b = 0
>>> a.static_method(b)
1
>>> a.static_method(a.c) # Static method does not have direct access to
>>> # class or instance properties.
3
>>> Class.c # a.c above was passed by value and not by reference.
2
>>> a.c
2
>>> a.c = 5 # The connection between the instance
>>> Class.c # and its class is weak as seen here.
2
>>> Class.class_method()
3
>>> a.c
5
メンバー関数を定義しているが、関数が何のメンバーであるかを伝えていないため、method_two は機能しません。最後の行を実行すると、次のようになります。
>>> a_test.method_two()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)
クラスのメンバー関数を定義している場合、最初の引数は常に「self」でなければなりません。
私のような初心者がよく理解できるように、上記の 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(リスト、str、関数などのインスタンスである可能性があります)であり、ランタイムによって提供されますinstance
この CPO が定義されているクラスのインスタンス (上記のオブジェクト「c」) であり、明示的に指定する必要があります。owner
は、この CPO が定義されているクラス (上記のクラス オブジェクト 'C') であり、私たちが提供する必要があります。ただし、これは CPO で呼び出しているためです。インスタンスで呼び出す場合、ランタイムはインスタンスまたはそのクラスを提供できるため(ポリモーフィズム)、これを提供する必要はありませんvalue
は CPO の意図した値であり、Google が提供する必要がありますすべての 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)
したがって、メソッド シグネチャが実際には this であるため、引数 self inが必要ですC.__dict__['foo'].__get__(c, C)
(上記で説明したように、C は検出またはポリモーフィングできるため、必要ありません)。また、必要なインスタンス引数を渡さないと TypeError が発生するのもこのためです。
メソッドがまだクラス Object C を介して参照されていることに気付いた場合、クラス インスタンスとのバインディングは、インスタンス オブジェクトの形式でコンテキストをこの関数に渡すことによって達成されます。
コンテキストを保持しない、またはインスタンスへのバインディングを保持しないことを選択した場合、必要なのは記述子 CPO をラップするクラスを作成し、その__get__()
メソッドをオーバーライドしてコンテキストを必要としないようにすることだけだったので、これは非常に素晴らしいことです。この新しいクラスはデコレータと呼ばれるもので、キーワードを介して適用されます@staticmethod
class C(object):
@staticmethod
def foo():
pass
新しいラップされた CPO にコンテキストが存在しない場合foo
、エラーはスローされず、次のように確認できます。
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
静的メソッドの使用例は、名前空間とコードの保守性 (クラスから取り出してモジュール全体で使用できるようにするなど) に近いものです。
もちろん、メソッドをコンテキスト化する必要がない限り (インスタンス変数やクラス変数へのアクセスなど)、可能な限りインスタンス メソッドではなく静的メソッドを作成することをお勧めします。理由の 1 つは、オブジェクトへの不要な参照を保持しないことでガベージ コレクションを容易にすることです。
method_two を呼び出すと、Python ランタイムが自動的に渡す self パラメーターを受け入れないという例外がスローされます。
Python クラスで静的メソッドを作成する場合は、staticmethod decorator
.
Class Test(Object):
@staticmethod
def method_two():
print "Called method_two"
Test.method_two()
それはエラーです。
まず、最初の行はこのようにする必要があります(大文字に注意してください)
class Test(object):
クラスのメソッドを呼び出すたびに、それ自体が最初の引数として取得され(したがって、selfという名前になります)、method_twoはこのエラーを返します
>>> a.method_two()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: method_two() takes no arguments (1 given)
そのように呼び出すと、Pythonは内部的にa_testインスタンスを最初の引数として呼び出しようとしますが、method_twoは引数を受け入れないため、機能しません。ランタイムを取得するため、2番目のものは機能しませんエラー。静的メソッドに相当するものが必要な場合は、クラス メソッドを使用できます。Java や C# などの言語での静的メソッドよりも、Python でのクラス メソッドの必要性ははるかに少なくなります。ほとんどの場合、最良の解決策は、クラス定義の外でモジュール内のメソッドを使用することです。これらのメソッドは、クラス メソッドよりも効率的に機能します。