Pythonのクラスメソッドの違い:バインド、非バインド、静的


242

次のクラスメソッドの違いは何ですか?

1つは静的で、もう1つは静的ではありませんか?

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()

18
method_two()定義以外の違いは無効ではなく、その呼び出しは失敗します。
anatly techtonik

14
@techtonik:method_twoの定義に問題はありません!正しくない/無効な仕様で、つまり追加の引数で呼び出されています。
0xc0de

1
あなたはどちらもインスタンスメソッドであり、クラスメソッドではありません。定義に適用してクラスメソッドを作成@classmethodします。最初のパラメータはcls代わりに呼び出されるべきでselfあり、あなたのクラスのインスタンスではなくクラスオブジェクトを受け取ります:Test.method_three()a_test.method_three()同等です。
Lutz Prechelt 2016

なぜself引数なしで関数定義を作成したいのですか?これの強力なユースケースはありますか?
alpha_989 2018年

回答:


412

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

17
私はこの答えを支持します、それは私の答えよりも優れています。よくやったTorsten :)
freespace '09 / 09/22

24
Python 3では、unboundメソッドは非推奨です。代わりに、関数があります。
boldnik 2013年

@boldnik、バインドされていないメソッドが廃止予定であると言うのはなぜですか?静的メソッドはまだドキュメントにあります:docs.python.org/3/library/functions.html#staticmethod
alpha_989

195

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>

それがそれを説明することを願っています。


12
staticmethodデコレータは(...)あなたのクラスをラップし、このフレーズをラップされたクラスがクラスであるとしてビット誤解で方法fooとないているクラスfooに定義されています。
Piotr Dobrogost 2013年

12

クラスメンバーを呼び出すと、Pythonは自動的にオブジェクトへの参照を最初のパラメーターとして使用します。変数はself実際には何も意味しません。それは単なるコーディング規約です。gargaloo必要に応じてそれを呼び出すことができます。そうは言っても、Pythonは自動的にパラメーター(その親オブジェクトへの参照)をパラメーターなしとして定義されたメソッドに渡そうとしているため、を呼び出すとmethod_twoが発生しTypeErrorます。

実際に機能させるには、これをクラス定義に追加します。

method_two = staticmethod(method_two)

または、@staticmethod 関数デコレータを使用できます。


4
「@staticmethod関数デコレータ構文」を意味します。
tzot 2008

11
>>> 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

2
Class.instance_method() # The class cannot use an instance method使用できます。ただ、手動でインスタンスを渡す:Class.instance_method(a)
warvariuc

@warwarukそこにありますTyeError。線の下の線を見てください。
kzh

はい、後で見ました。それでも、imo、「クラスはインスタンスメソッドを使用できません」と言うのは正しくありません。1行下で実行しただけだからです。
warvariuc 2012年

@kzh、ご説明ありがとうございます。あなたが呼び出されるとa.class_method()、それは思わa.cに更新してしまった1ため、コールはClass.class_method()、更新Class.cに変数を2。しかし、あなたが割り当てたときa.c=5、なぜClass.c更新されなかったの5ですか?
alpha_989 2018年

@ alpha_989 pythonは、最初にovjects自身のインスタンスで直接属性を探し、そこにない場合は、デフォルトでそのクラスで属性を探します。この件に関して他にご不明な点がありましたら、お気軽に質問を開いてこちらにリンクしてください。サポートさせていただきます。
kzh

4

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'でなければなりません。


3

上記の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つの理由は、オブジェクトへの不要な参照を保持しないことにより、ガベージコレクションを容易にするためです。


1

それはエラーです。

まず、最初の行はこのようにする必要があります(大文字に注意してください)

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)

1

2番目の方法は機能しません。そのような方法で呼び出すと、Pythonが最初の引数としてa_testインスタンスを使用してそれを呼び出そうとしますが、method_twoは引数を受け入れないため、機能せず、ランタイムを取得します。エラー。静的メソッドと同等のものが必要な場合は、クラスメソッドを使用できます。Pythonのクラスメソッドの必要性は、JavaやC#などの言語の静的メソッドよりもはるかに少なくなります。ほとんどの場合、最良の解決策は、クラス定義の外でモジュール内のメソッドを使用することです。これらのメソッドは、クラスメソッドよりも効率的に機能します。


関数を定義する場合 Class Test(object): @staticmethod def method_two(): print(“called method_two”) 1つの使用例として、関数をクラスの一部にしたいが、ユーザーが関数に直接アクセスできないようにする場合を考えていました。したがって、インスタンスmethod_two内の他の関数からを呼び出すことはできますが、を使用して呼び出すことはできません。を使用する場合、これはこの使用例で機能しますか?または、関数定義を変更するより良い方法があるので、上記のユースケースで意図したとおりに機能しますか?Testa_test.method_two()def method_two()
alpha_989 2018年

1

method_twoを呼び出すと、Pythonランタイムが自動的に渡すselfパラメータを受け入れないという例外がスローされます。

Pythonクラスで静的メソッドを作成する場合は、それをで装飾しstaticmethod decoratorます。

Class Test(Object):
  @staticmethod
  def method_two():
    print "Called method_two"

Test.method_two()


0

の定義method_twoが無効です。を呼び出すとmethod_twoTypeError: method_two() takes 0 positional arguments but 1 was given通訳から取得します。

インスタンスメソッドは、のように呼び出すと有界関数になりますa_test.method_two()。最初のパラメータとしてself、のインスタンスを指すを自動的に受け入れTestます。selfパラメータを介して、インスタンスメソッドは属性に自由にアクセスし、同じオブジェクトの属性を変更できます。


0

バインドされていないメソッド

非バインドメソッドは、特定のクラスインスタンスにまだバインドされていないメソッドです。

バインドされたメソッド

バインドされたメソッドは、クラスの特定のインスタンスにバインドされるメソッドです。

ここに記載されているようににているように、関数がバインドされているか、バインドされていないか、静的であるかによって、selfはさまざまなものを参照できます。

次の例を見てください。

class MyClass:    
    def some_method(self):
        return self  # For the sake of the example

>>> MyClass().some_method()
<__main__.MyClass object at 0x10e8e43a0># This can also be written as:>>> obj = MyClass()

>>> obj.some_method()
<__main__.MyClass object at 0x10ea12bb0>

# Bound method call:
>>> obj.some_method(10)
TypeError: some_method() takes 1 positional argument but 2 were given

# WHY IT DIDN'T WORK?
# obj.some_method(10) bound call translated as
# MyClass.some_method(obj, 10) unbound method and it takes 2 
# arguments now instead of 1 

# ----- USING THE UNBOUND METHOD ------
>>> MyClass.some_method(10)
10

クラスインスタンスを使用しなかったため、 obj最後の呼び出しでは、静的メソッドのように見えます。

もしそうなら、MyClass.some_method(10)呼び出しとで装飾された静的関数の呼び出しとの違いは何ですか@staticmethodデコレータで装飾ですか?

デコレータを使用することにより、最初にインスタンスを作成せずにメソッドが使用されることを明示的に明確にします。通常、クラスメンバーメソッドがインスタンスなしで使用されることは想定されていません。これらのメソッドにアクセスすると、メソッドの構造によってはエラーが発生する可能性があります。

また、@staticmethodデコレータを追加することで、オブジェクトからも到達できるようにしています。

class MyClass:    
    def some_method(self):
        return self    

    @staticmethod
    def some_static_method(number):
        return number

>>> MyClass.some_static_method(10)   # without an instance
10
>>> MyClass().some_static_method(10)   # Calling through an instance
10

上記の例をインスタンスメソッドで実行することはできません。(私たちは前に行ったように)あなたが最初のものを乗り切るかもしれないが、もう一つは、未結合の呼び出しに変換されますMyClass.some_method(obj, 10)引き上げるTypeErrorインスタンスメソッドは一つの引数を取り、あなたが意図せずに2を渡そうとしましたので、。

その後、あなたは「私は、インスタンスとクラスの両方を通じて静的メソッドを呼び出すことができ、場合、言うかもしれないMyClass.some_static_methodMyClass().some_static_method同じ方法でなければなりません。」はい!


0

バインドされたメソッド=インスタンスメソッド

非バインドメソッド=静的メソッド。


他の人が答えを学べるように、回答にさらに説明を追加してください。たとえば、この質問の他の回答を見てみましょう
ニコ・ハーセ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.