1784

誰かが Pythonの@classmethodandの意味を説明してくれませんか? @staticmethod違いと意味が知りたいです。

私が理解している限り、@classmethodそれはサブクラスに継承する必要があるメソッドであるか、または...何かであることをクラスに伝えます。しかし、そのポイントは何ですか?@classmethodまたは@staticmethodまたは定義を追加せずに、クラスメソッドを定義するだけでは@どうですか?

tl;dr: いつ使用する必要があるか、なぜ使用する必要があるか、どのように使用する必要がありますか?

4

12 に答える 12

2807

classmethodstaticmethodは非常に似ていますが、両方のエンティティの使用法にはわずかな違いがclassmethodあります。最初のパラメーターとしてクラス オブジェクトへの参照が必要ですが、staticmethodパラメーターをまったく持つことはできません。

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

date2 = Date.from_string('11-09-2012')
is_date = Date.is_date_valid('11-09-2012')

説明

日付情報を扱うクラスの例を考えてみましょう (これがボイラープレートになります)。

class Date(object):

    def __init__(self, day=0, month=0, year=0):
        self.day = day
        self.month = month
        self.year = year

このクラスは明らかに、特定の日付に関する情報を格納するために使用できます (タイムゾーン情報なし。すべての日付が UTC で表示されると仮定しましょう)。

ここに__init__、Python クラス インスタンスの典型的な初期化子である があり、これは典型的な として引数を受け取り、新しく作成されたインスタンスへの参照を保持するinstancemethod最初のオプションではない引数 ( ) を持ちます。self

クラス メソッド

sを使用して適切に実行できるタスクがいくつかありますclassmethod

Date「dd-mm-yyyy」形式の文字列としてエンコードされた外部ソースからの日付情報を持つ多くのクラス インスタンスを作成するとします。プロジェクトのソース コードのさまざまな場所でこれを行う必要があるとします。

したがって、ここで行う必要があるのは次のとおりです。

  1. 文字列を解析して、日、月、年を 3 つの整数変数またはその変数で構成される 3 項目のタプルとして受け取ります。
  2. Dateこれらの値を初期化呼び出しに渡してインスタンス化します。

これは次のようになります。

day, month, year = map(int, string_date.split('-'))
date1 = Date(day, month, year)

この目的のために、C++ はそのような機能をオーバーロードで実装できますが、Python にはこのオーバーロードがありません。代わりに、 を使用できますclassmethod別の「コンストラクタ」を作成しましょう。

    @classmethod
    def from_string(cls, date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        date1 = cls(day, month, year)
        return date1

date2 = Date.from_string('11-09-2012')

上記の実装をより注意深く見て、ここにどのような利点があるかを確認しましょう。

  1. 日付文字列の解析を 1 か所に実装し、再利用できるようになりました。
  2. カプセル化はここで問題なく機能します (文字列解析を他の場所で単一の関数として実装できると思われる場合、このソリューションは OOP パラダイムにはるかに適しています)。
  3. clsクラスのインスタンスではなく、クラス自体を保持するオブジェクトです。Dateクラスを継承すると、すべての子もfrom_string定義されるため、非常にクールです。

静的メソッド

どうstaticmethodですか?これは によく似てclassmethodいますが、(クラス メソッドやインスタンス メソッドのように) 必須パラメーターを取りません。

次の使用例を見てみましょう。

何らかの方法で検証したい日付文字列があります。このタスクも、これまで使用してきたクラスに論理的にバインドされてDateいますが、インスタンス化は必要ありません。

ここがstaticmethod役に立ちます。次のコードを見てみましょう。

    @staticmethod
    def is_date_valid(date_as_string):
        day, month, year = map(int, date_as_string.split('-'))
        return day <= 31 and month <= 12 and year <= 3999

    # usage:
    is_date = Date.is_date_valid('11-09-2012')

したがって、 の使用法からわかるstaticmethodように、クラスが何であるかにアクセスすることはできません。これは基本的にはメソッドのように構文的に呼び出される単なる関数ですが、オブジェクトとその内部 (フィールドと別のクラス) へのアクセスはありません。メソッド)、classmethod はそうします。

于 2012-08-29T14:03:03.613 に答える
938

Rostyslav Dzinko の答えは非常に適切です。追加のコンストラクターを作成するときに選択@classmethodする必要があるもう 1 つの理由を強調できると思いました。@staticmethod

上記の例では、Rostyslav は を Factory として使用して、他の方法では受け入れられないパラメーターからオブジェクト@classmethod from_stringを作成しました。以下のコードに示すように、Date同じことができます。@staticmethod

class Date:
  def __init__(self, month, day, year):
    self.month = month
    self.day   = day
    self.year  = year


  def display(self):
    return "{0}-{1}-{2}".format(self.month, self.day, self.year)


  @staticmethod
  def millenium(month, day):
    return Date(month, day, 2000)

new_year = Date(1, 1, 2013)               # Creates a new Date object
millenium_new_year = Date.millenium(1, 1) # also creates a Date object. 

# Proof:
new_year.display()           # "1-1-2013"
millenium_new_year.display() # "1-1-2000"

isinstance(new_year, Date) # True
isinstance(millenium_new_year, Date) # True

したがって、new_yearとは両方ともクラスmillenium_new_yearのインスタンスです。Date

Dateしかし、よく観察すると、Factory プロセスは何があってもオブジェクトを作成するようにハードコーディングされています。これが意味することは、Dateクラスがサブクラス化されていても、サブクラスはプレーンなDateオブジェクトを (サブクラスのプロパティなしで) 作成するということです。以下の例を参照してください。

class DateTime(Date):
  def display(self):
      return "{0}-{1}-{2} - 00:00:00PM".format(self.month, self.day, self.year)


datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # False

datetime1.display() # returns "10-10-1990 - 00:00:00PM"
datetime2.display() # returns "10-10-2000" because it's not a DateTime object but a Date object. Check the implementation of the millenium method on the Date class for more details.

datetime2のインスタンスではありませんDateTimeか? なんてこと?まあ、それは@staticmethodデコレータが使われているからです。

ほとんどの場合、これは望ましくありません。必要なものが、それを呼び出したクラスを認識している Factory メソッドである場合は、それ@classmethodが必要です。

次のように書き換えDate.milleniumます (変更されるのは、上記のコードの唯一の部分です)。

@classmethod
def millenium(cls, month, day):
    return cls(month, day, 2000)

classがハードコードされているのではなく、学習されていることを保証します。cls任意のサブクラスにすることができます。結果objectは当然 のインスタンスになりますcls
それをテストしましょう:

datetime1 = DateTime(10, 10, 1990)
datetime2 = DateTime.millenium(10, 10)

isinstance(datetime1, DateTime) # True
isinstance(datetime2, DateTime) # True


datetime1.display() # "10-10-1990 - 00:00:00PM"
datetime2.display() # "10-10-2000 - 00:00:00PM"

その理由は、ご存知@classmethodのように、@staticmethod

于 2013-01-30T13:35:39.083 に答える
275

@classmethodつまり、このメソッドが呼び出されると、そのクラスのインスタンスではなく、クラスを最初の引数として渡します (通常はメソッドで行います)。これは、特定のインスタンスではなく、そのメソッド内でクラスとそのプロパティを使用できることを意味します。

@staticmethodつまり、このメソッドが呼び出されたときに、クラスのインスタンスをそれに渡しません (通常はメソッドで行います)。これは、クラス内に関数を配置できますが、そのクラスのインスタンスにアクセスできないことを意味します (これは、メソッドがインスタンスを使用しない場合に役立ちます)。

于 2012-08-29T13:40:24.343 に答える
110

それぞれいつ使うか

@staticmethod関数は、クラス内で定義された関数にすぎません。最初にクラスをインスタンス化せずに呼び出すことができます。その定義は継承によって不変です。

  • Python は、オブジェクトのバインドされたメソッドをインスタンス化する必要はありません。
  • @staticmethodを見ると、メソッドがオブジェクト自体の状態に依存しないことがわかります。

@classmethod関数はクラスをインスタンス化せずに呼び出すこともできますが、その定義は親クラスではなくサブクラスに従います。継承により、サブクラスによってオーバーライドできます。これは、@classmethod関数の最初の引数が常に でなければならないためcls (class)です。

  • たとえば、ある種の前処理を使用してクラスのインスタンスを作成するために使用されるファクトリ メソッド。
  • 静的メソッドを呼び出す静的メソッド: 静的メソッドを複数の静的メソッドに分割する場合は、クラス名をハードコードするのではなく、クラス メソッドを使用する必要があります。

hereは、このトピックへの適切なリンクです。

于 2014-04-14T07:12:38.953 に答える
59

@classmethod@staticmethodの意味

  • メソッドは、オブジェクトの名前空間内の関数であり、属性としてアクセスできます。
  • 通常の (インスタンス) メソッドは、インスタンス (通常は と呼びますself) を暗黙の最初の引数として取得します。
  • クラスメソッドは、クラス (通常は と呼びますcls) を暗黙の最初の引数として取得します。
  • 静的メソッドは、 (通常の関数のように) 暗黙的な最初の引数を取得しません。

いつ、なぜ使用する必要があり、どのように使用する必要がありますか?

どちらのデコレータも必要ありません。しかし、関数への引数の数を最小限に抑えるべきであるという原則 (Clean Coder を参照) では、関数はまさにそれを行うのに役立ちます。

class Example(object):

    def regular_instance_method(self):
        """A function of an instance has access to every attribute of that 
        instance, including its class (and its attributes.)
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_f(self)

    @classmethod
    def a_class_method(cls):
        """A function of a class has access to every attribute of the class.
        Not accepting at least one argument is a TypeError.
        Not understanding the semantics of that argument is a user error.
        """
        return some_function_g(cls)

    @staticmethod
    def a_static_method():
        """A static method has no information about instances or classes
        unless explicitly given. It just lives in the class (and thus its 
        instances') namespace.
        """
        return some_function_h()

インスタンス メソッドとクラス メソッドの両方で、少なくとも 1 つの引数を受け入れないことは TypeError ですが、その引数のセマンティクスを理解していないことはユーザー エラーです。

( を定義some_functionします。例:

some_function_h = some_function_g = some_function_f = lambda x=None: x

これは機能します。)

インスタンスとクラスのドット ルックアップ:

インスタンスのドット ルックアップは、次の順序で実行されます。

  1. クラス名前空間のデータ記述子 (プロパティなど)
  2. インスタンス内のデータ__dict__
  3. クラス名前空間 (メソッド) の非データ記述子。

インスタンスのドット ルックアップは次のように呼び出されることに注意してください。

instance = Example()
instance.regular_instance_method 

そしてメソッドは呼び出し可能な属性です:

instance.regular_instance_method()

インスタンスメソッド

引数 はself、ドット ルックアップを介して暗黙的に指定されます。

クラスのインスタンスからインスタンス メソッドにアクセスする必要があります。

>>> instance = Example()
>>> instance.regular_instance_method()
<__main__.Example object at 0x00000000399524E0>

クラスメソッド

引数 はcls、ドット ルックアップによって暗黙的に指定されます。

このメソッドには、インスタンスまたはクラス (またはサブクラス) を介してアクセスできます。

>>> instance.a_class_method()
<class '__main__.Example'>
>>> Example.a_class_method()
<class '__main__.Example'>

静的メソッド

引数は暗黙的に与えられません。このメソッドは、ルックアップできることを除けば、モジュールの名前空間で (たとえば) 定義された関数と同じように機能します。

>>> print(instance.a_static_method())
None

繰り返しますが、いつ使用する必要がありますか? なぜ使用する必要があるのですか?

これらはそれぞれ、インスタンス メソッドに対してメソッドに渡す情報を徐々に制限しています。

情報が必要ない場合に使用します。

これにより、関数とメソッドの推論と単体テストが容易になります。

どちらが推論しやすいですか?

def function(x, y, z): ...

また

def function(y, z): ...

また

def function(z): ...

引数が少ない関数ほど、推論が容易になります。また、単体テストも簡単です。

これらは、インスタンス、クラス、および静的メソッドに似ています。インスタンスがある場合、そのクラスもあることに注意してください。もう一度自問してみてください。どちらが推論しやすいでしょうか?

def an_instance_method(self, arg, kwarg=None):
    cls = type(self)             # Also has the class of instance!
    ...

@classmethod
def a_class_method(cls, arg, kwarg=None):
    ...

@staticmethod
def a_static_method(arg, kwarg=None):
    ...

組み込みの例

以下に、私のお気に入りの組み込みの例をいくつか示します。

str.maketrans静的メソッドはモジュール内の関数でしたが、名前空間stringからアクセスできる方がはるかに便利です。str

>>> 'abc'.translate(str.maketrans({'a': 'b'}))
'bbc'

dict.fromkeysクラス メソッドは、反復可能なキーからインスタンス化された新しい辞書を返します。

>>> dict.fromkeys('abc')
{'a': None, 'c': None, 'b': None}

サブクラス化すると、クラス情報をクラス メソッドとして取得することがわかります。これは非常に便利です。

>>> class MyDict(dict): pass
>>> type(MyDict.fromkeys('abc'))
<class '__main__.MyDict'> 

私のアドバイス - 結論

クラスまたはインスタンスの引数が不要であるが、関数がオブジェクトの使用に関連しており、関数がオブジェクトの名前空間にあると便利な場合は、静的メソッドを使用します。

インスタンス情報は必要ないが、他のクラスや静的メソッド、またはコンストラクターとしてのクラス情報が必要な場合は、クラス メソッドを使用します。(ここでサブクラスを使用できるように、クラスをハードコーディングしないでください。)

于 2017-10-24T16:09:46.757 に答える
38

@classmethodメソッドを呼び出しているサブクラスに基づいてメソッドの動作を変更したい場合に使用します。クラス メソッド内に呼び出し元クラスへの参照があることを思い出してください。

static を使用している間、サブクラス全体で動作を変更しないでおきたい場合があります

例:

class Hero:

  @staticmethod
  def say_hello():
     print("Helllo...")

  @classmethod
  def say_class_hello(cls):
     if(cls.__name__=="HeroSon"):
        print("Hi Kido")
     elif(cls.__name__=="HeroDaughter"):
        print("Hi Princess")

class HeroSon(Hero):
  def say_son_hello(self):
     print("test  hello")



class HeroDaughter(Hero):
  def say_daughter_hello(self):
     print("test  hello daughter")


testson = HeroSon()

testson.say_class_hello() #Output: "Hi Kido"

testson.say_hello() #Outputs: "Helllo..."

testdaughter = HeroDaughter()

testdaughter.say_class_hello() #Outputs: "Hi Princess"

testdaughter.say_hello() #Outputs: "Helllo..."
于 2015-08-21T07:18:24.240 に答える
1

私はこのサイトの初心者です。上記の回答をすべて読み、必要な情報を取得しました。ただし、賛成票を投じる権利はありません。だから、私が理解している答えでStackOverflowを始めたいと思います。

  • @staticmethodメソッドの最初のパラメーターとして self または cls は必要ありません
  • @staticmethodラップされた関数は、@classmethodインスタンスまたはクラス変数によって呼び出すことができます
  • @staticmethod@staticmethod装飾された関数は、サブクラスの継承がデコレータによってラップされた基本クラスの関数を上書きできないというある種の「不変プロパティ」に影響を与えます。
  • @classmethod関数の最初のパラメータとしてcls(クラス名、必要に応じて変数名を変更できますが、お勧めしません)が必要です
  • @classmethod常にサブクラスの方法で使用されますが、サブクラスの継承により、基本クラス関数の効果が変わる可能性があります。つまり、@classmethodラップされた基本クラス関数が異なるサブクラスによって上書きされる可能性があります。
于 2017-07-23T11:38:23.723 に答える
0

誰かにとって役立つかもしれない少し異なる考え方...クラスメソッドは、スーパークラスで使用され、異なる子クラスから呼び出されたときにそのメソッドがどのように動作するかを定義します。呼び出している子クラスに関係なく同じものを返したい場合は、静的メソッドが使用されます。

于 2017-01-24T22:59:50.330 に答える