誰かが私にPythonで@classmethod
と@staticmethod
の意味を説明してもらえますか?違いと意味を知る必要があります。
私が理解している限りでは、@classmethod
はクラスに、それがサブクラスに継承されるべきなのか、あるいは何かなのかを伝えます。しかし、そのポイントは何ですか? @classmethod
や@staticmethod
あるいは@
の定義を追加せずにクラスメソッドを定義しないのはなぜですか。
tl; dr: いつ を使うべきか、 なぜ を使うべきか、 how を使うべきか
私はC++をかなり進んでいるので、もっと高度なプログラミング概念を使っても問題にならないはずです。可能であれば、対応するC++の例を教えてください。
classmethod
とstaticmethod
は非常に似ていますが、両方のエンティティの使用法にわずかな違いがあります。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で提示されると仮定しましょう)。
ここには、Pythonクラスインスタンスの典型的な初期化子である__init__
があります。これは典型的なinstancemethod
として引数を受け取り、新しく作成されたインスタンスへの参照を保持する最初の非オプション引数(self
)を持ちます。
クラスメソッド
classmethod
sを使ってうまくできるタスクがいくつかあります。
'dd-mm-yyyy'という形式の文字列としてエンコードされた外部ソースからの日付情報を持つDate
クラスのインスタンスをたくさん作成したいとしましょう。プロジェクトのソースコードのさまざまな場所でこれを実行する必要があるとします。
だからここでやらなければならないことは、
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')
上記の実装をもっと注意深く見て、私たちがここで持っているどんな利点を見直しましょう:
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が行いますが、別の方法)。
Rostyslav Dzinkoの答えはとても適切です。追加のコンストラクタを作成するときに@classmethod
より@staticmethod
を選択する必要があるというもう1つの理由を強調できると思いました。
上の例では、Rostyslavは@classmethod
from_string
をFactoryとして使用して、それ以外の方法では受け入れられないパラメータから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
クラスのインスタンスです。
しかし、よく観察すると、FactoryプロセスはDate
オブジェクトを作成するためにハードコードされています。これが意味することは、たとえ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
datetime2
はDateTime
のインスタンスではありませんか? WTF?それは@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
が使用されたためです。
@classmethod
は、このメソッドが呼び出されたときに、そのクラスのインスタンスではなく、クラスを最初の引数として渡すことを意味します(通常はメソッドで行います)。つまり、特定のインスタンスではなく、そのメソッド内でクラスとそのプロパティを使用できます。
@staticmethod
は、このメソッドが呼び出されたときに、クラスのインスタンスをそれに渡すことがないことを意味します(通常はメソッドで行うように)。つまり、クラス内に関数を入れることはできますが、そのクラスのインスタンスにアクセスすることはできません(これは、メソッドがインスタンスを使用しない場合に役立ちます)。
@staticmethod
関数はクラス内で定義された関数にすぎません。最初にクラスをインスタンス化せずに呼び出すことができます。その定義は継承によって不変です。
@classmethod
関数はクラスをインスタンス化せずに呼び出すこともできますが、その定義は継承を通じてParentクラスではなくSubクラスに従いますが、サブクラスでオーバーライドできます。これは、@classmethod
関数の最初の引数が常にcls (class)
でなければならないためです。
ここ はこのトピックへのリンクです。
@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
これでうまくいくでしょう。)
インスタンス上のドット付きルックアップはこの順序で実行されます。
__dict__
インスタンスのドット付きルックアップは次のように呼び出されます。
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
繰り返しますが、いつ使用する必要がありますか。なぜ使用する必要があるのですか。
これらのそれぞれは、それらがメソッドを通過する情報とインスタンスメソッドの間で次第に制限が厳しくなります。
あなたが情報を必要としないときにそれらを使ってください。
これにより、関数やメソッドを推論したりunittestしたりするのが簡単になります。
どちらが推論しやすいですか?
def function(x, y, z): ...
または
def function(y, z): ...
または
def function(z): ...
引数が少ない関数の方が推論が容易です。それらはまたunittestするのがより簡単です。
これらはインスタンス、クラス、そして静的メソッドに似ています。インスタンスがあるときは、そのクラスもあることを忘れないでください。
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'>
クラスやインスタンスの引数が不要な場合は静的メソッドを使用しますが、関数はオブジェクトの使用に関連しているため、関数がオブジェクトの名前空間にあると便利です。
インスタンス情報を必要としないが、おそらく他のクラスまたは静的メソッドのクラス情報を必要とする場合、またはおそらくそれ自体をコンストラクターとして必要とする場合は、クラスメソッドを使用します。 (サブクラスをここで使用できるように、クラスをハードコードしないでください。)
どのサブクラスがメソッドを呼び出しているかに基づいてメソッドの動作を変更したい場合は、@classmethod
を使用します。呼び出し元クラスへの参照がクラスメソッド内にあることを忘れないでください。
静的を使用している間は、サブクラス間で振る舞いを変えないでください。
例:
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..."
@ classmethod
@classmethod
は__init__
と比較できます。別の__init__()
と考えることができます。 pythonは、c ++でクラスコンストラクターのオーバーロードを実現する方法です。
class C:
def __init__(self, parameters):
....
@classmethod
def construct_from_func(cls, parameters):
....
obj1 = C(parameters)
obj2 = C.construct_from_func(parameters)
__init__
はself
を使用しますが、construct_from_func
はcls
を使用しますが、どちらもdefinitioinの最初の引数としてクラスの参照を持っていることに注意してください。
@ staticmethod
@staticmethod
はobject method
と比較できます
class C:
def __init__(self):
....
@staticmethod
def static_method(args):
....
def normal_method(parameters):
....
result = C.static_method(parameters)
result = obj.normal_method(parameters)
つまり、@ classmehtodは通常のメソッドをファクトリメソッドに変換します。
例を使って探りましょう。
class PythonBook:
def __init__(self, name, author):
self.name = name
self.author = author
def __repr__(self):
return f'Book: {self.name}, Author: {self.author}'
@クラスメソッドがないと、インスタンスを1つずつ作成するのに手間がかかるはずです。
book1 = PythonBook('Learning Python', 'Mark Lutz')
In [20]: book1
Out[20]: Book: Learning Python, Author: Mark Lutz
book2 = PythonBook('Python Think', 'Allen B Dowey')
In [22]: book2
Out[22]: Book: Python Think, Author: Allen B Dowey
例えば@classmethodで
class PythonBook:
def __init__(self, name, author):
self.name = name
self.author = author
def __repr__(self):
return f'Book: {self.name}, Author: {self.author}'
@classmethod
def book1(cls):
return cls('Learning Python', 'Mark Lutz')
@classmethod
def book2(cls):
return cls('Python Think', 'Allen B Dowey')
試して:
In [31]: PythonBook.book1()
Out[31]: Book: Learning Python, Author: Mark Lutz
In [32]: PythonBook.book2()
Out[32]: Book: Python Think, Author: Allen B Dowey
見る?インスタンスはクラス定義内に正常に作成され、まとめて収集されます。
結論として、@ classmethodデコレータは従来のメソッドをファクトリメソッドに変換します。クラスメソッドを使用すると、必要なだけ代替コンストラクタを追加することができます。
私はこのサイトの初心者です、私は上のすべての答えを読みました、そして私が欲しい情報を得ました。しかし、私は投票する権利がありません。それで、私はそれを理解しているので答えと共にStackOverflowを始めようと思います。
@staticmethod
は、メソッドの最初のパラメータとしてselfやclsを必要としません。@staticmethod
および@classmethod
ラップされた関数は、インスタンスまたはクラス変数によって呼び出される可能性があります@staticmethod
装飾関数は、サブクラスの継承が@staticmethod
デコレータによってラップされている基本クラス関数を上書きできないというある種の「不変のプロパティ」に影響します。@classmethod
は、関数の最初のパラメータとしてcls(クラス名、必要ならば変数名を変更することができます)を必要とします@classmethod
は常にサブクラス方式で使用されます。サブクラスの継承は基本クラス関数の効果を変更する可能性があります。つまり、@classmethod
ラップされた基本クラス関数は異なるサブクラスによって上書きされる可能性があります。誰かに役立つかもしれない少し異なる考え方...クラスメソッドは、スーパークラスで使用され、異なる子クラスから呼び出されたときのメソッドの動作を定義します。静的メソッドは、呼び出している子クラスに関係なく同じものを返したいときに使用されます。