267

私は独学で Python を学んでおり、最近の教訓はPython は Javaではないということでした。そのため、しばらく時間をかけてすべての Class メソッドを関数に変換しました。

Java でメソッドを使用する場合に Class メソッドを使用する必要がないことに気付きましたがstatic、いつ使用するかはわかりません。Python クラス メソッドについて私が見つけることができるすべてのアドバイスは、私のような初心者の方針に沿ったものであり、標準ドキュメントはそれらについて議論するときに最も不透明です。

誰かが Python で Class メソッドを使用する良い例を持っていますか、または少なくとも誰かが Class メソッドをいつ賢明に使用できるかを教えてくれますか?

4

18 に答える 18

197

クラス メソッドは、特定のインスタンスに固有ではないものの、何らかの方法でクラスに関与するメソッドが必要な場合に使用します。それらの最も興味深い点は、サブクラスによってオーバーライドできることです。これは、Java の静的メソッドや Python のモジュール レベルの関数では不可能なことです。

classMyClassと、MyClass で動作するモジュール レベルの関数 (ファクトリ、依存性注入スタブなど) がある場合は、それをclassmethod. その後、サブクラスで使用できるようになります。

于 2008-09-01T18:45:56.807 に答える
66

ファクトリメソッド(代替コンストラクター)は、確かにクラスメソッドの典型的な例です。

基本的に、クラスメソッドは、クラスの名前空間に自然に適合するが、クラスの特定のインスタンスに関連付けられていないメソッドが必要な場合に適しています。

例として、優れたユニパスモジュールでは次のようになります。

カレントディレクトリ

  • Path.cwd()
    • 実際の現在のディレクトリを返します。例:Path("/tmp/my_temp_dir")。これはクラスメソッドです。
  • .chdir()
    • 自分自身を現在のディレクトリにします。

現在のディレクトリはプロセス全体であるため、cwdメソッドには、関連付ける必要のある特定のインスタンスはありません。ただし、をcwd特定のインスタンスのディレクトリに変更することは、Path実際にはインスタンスメソッドである必要があります。

うーん...Path.cwd()実際にインスタンスを返すPathように、ファクトリメソッドと見なすことができると思います...

于 2008-09-01T19:08:25.660 に答える
49

このように考えてみてください: 通常のメソッドは、ディスパッチの詳細を隠すのに役立ちます:メソッドがオブジェクトのクラスによって実装されているか、その親クラスの 1 つによって実装されてmyobj.foo()いるかを気にせずに入力できます。クラスメソッドはこれとまったく同じですが、代わりにクラスオブジェクトを使用します。独自の特殊化されたバージョンが必要なために によって特別に実装されているかどうか、または親クラスに呼び出しを処理させているかどうかを気にせずに呼び出すことができます。foo()myobjMyClass.foo()foo()MyClass

クラス メソッドは、実際のインスタンスの作成に先立ってセットアップまたは計算を行う場合に不可欠です。インスタンスが存在するまでは、明らかにインスタンスをメソッド呼び出しのディスパッチ ポイントとして使用できないからです。良い例は、SQLAlchemy のソース コードで見ることができます。dbapi()次のリンクでクラス メソッドを見てください。

https://github.com/zzzeek/sqlalchemy/blob/ab6946769742602e40fb9ed9dde5f642885d1906/lib/sqlalchemy/dialects/mssql/pymssql.py#L47

データベース バックエンドが必要なベンダー固有のデータベース ライブラリをオンデマンドでインポートするために使用するメソッドは、特定のデータベース接続のインスタンスが作成される前dbapi()に実行する必要があるため、クラス メソッドであることがわかりますが、それはできません。親クラスよりもサブクラスでより具体的に記述する必要がある他のサポートメソッドを呼び出すことができるようにするため、単純な関数または静的関数である必要があります。また、関数または静的クラスにディスパッチすると、「忘れて」、どのクラスが初期化を行っているかについての知識が失われます。

于 2010-08-19T12:51:27.397 に答える
30

私は最近、プログラムで設定できるロギング レベルに応じてさまざまな量の出力を出力する、非常に軽量なロギング クラスが必要でした。しかし、デバッグ メッセージ、エラー、または警告を出力するたびにクラスをインスタンス化する必要はありませんでした。しかし、このロギング機能の機能をカプセル化し、グローバルを宣言せずに再利用できるようにしたいとも考えていました。

そこで、クラス変数と@classmethodデコレータを使用してこれを実現しました。

単純な Logging クラスを使用すると、次のことができます。

Logger._level = Logger.DEBUG

次に、私のコードで、大量のデバッグ情報を吐き出したい場合は、単純にコーディングする必要がありました

Logger.debug( "this is some annoying message I only want to see while debugging" )

エラーは次のように出力できます

Logger.error( "Wow, something really awful happened." )

「本番」環境では、指定できます

Logger._level = Logger.ERROR

これで、エラー メッセージのみが出力されます。デバッグ メッセージは出力されません。

これが私のクラスです:

class Logger :
    ''' Handles logging of debugging and error messages. '''

    DEBUG = 5
    INFO  = 4
    WARN  = 3
    ERROR = 2
    FATAL = 1
    _level = DEBUG

    def __init__( self ) :
        Logger._level = Logger.DEBUG

    @classmethod
    def isLevel( cls, level ) :
        return cls._level >= level

    @classmethod
    def debug( cls, message ) :
        if cls.isLevel( Logger.DEBUG ) :
            print "DEBUG:  " + message

    @classmethod
    def info( cls, message ) :
        if cls.isLevel( Logger.INFO ) :
            print "INFO :  " + message

    @classmethod
    def warn( cls, message ) :
        if cls.isLevel( Logger.WARN ) :
            print "WARN :  " + message

    @classmethod
    def error( cls, message ) :
        if cls.isLevel( Logger.ERROR ) :
            print "ERROR:  " + message

    @classmethod
    def fatal( cls, message ) :
        if cls.isLevel( Logger.FATAL ) :
            print "FATAL:  " + message

そして、それを少しだけテストするいくつかのコード:

def logAll() :
    Logger.debug( "This is a Debug message." )
    Logger.info ( "This is a Info  message." )
    Logger.warn ( "This is a Warn  message." )
    Logger.error( "This is a Error message." )
    Logger.fatal( "This is a Fatal message." )

if __name__ == '__main__' :

    print "Should see all DEBUG and higher"
    Logger._level = Logger.DEBUG
    logAll()

    print "Should see all ERROR and higher"
    Logger._level = Logger.ERROR
    logAll()
于 2010-05-11T01:48:33.627 に答える
29

代替コンストラクターは典型的な例です。

于 2008-09-01T18:27:27.523 に答える
11

ユーザーが私の Web サイトにログインすると、ユーザー名とパスワードから User() オブジェクトがインスタンス化されます。

ログインするユーザーがいない状態でユーザー オブジェクトが必要な場合 (たとえば、管理者ユーザーが別のユーザー アカウントを削除したい場合、そのユーザーをインスタンス化してその削除メソッドを呼び出す必要があります):

ユーザー オブジェクトを取得するためのクラス メソッドがあります。

class User():
    #lots of code
    #...
    # more code

    @classmethod
    def get_by_username(cls, username):
        return cls.query(cls.username == username).get()

    @classmethod
    def get_by_auth_id(cls, auth_id):
        return cls.query(cls.auth_id == auth_id).get()
于 2012-03-26T06:36:52.593 に答える
10

これにより、互換性のあるクラスで使用できるジェネリック クラス メソッドを記述できます。

例えば:

@classmethod
def get_name(cls):
    print cls.name

class C:
    name = "tester"

C.get_name = get_name

#call it:
C.get_name()

使用しない場合は@classmethod、self キーワードで実行できますが、Class のインスタンスが必要です。

def get_name(self):
    print self.name

class C:
    name = "tester"

C.get_name = get_name

#call it:
C().get_name() #<-note the its an instance of class C
于 2012-12-26T15:11:20.733 に答える
10

最も明確な答えは、 AmanKow のものだと思います。要するに、コードをどのように整理したいかということです。モジュールの名前空間にラップされたモジュールレベルの関数としてすべてを書くことができます。

module.py (file 1)
---------
def f1() : pass
def f2() : pass
def f3() : pass


usage.py (file 2)
--------
from module import *
f1()
f2()
f3()
def f4():pass 
def f5():pass

usage1.py (file 3)
-------------------
from usage import f4,f5
f4()
f5()

上記の手続き型コードは、よく整理されていません。たった 3 つのモジュールでわかりにくくなりますが、各メソッドは何をするのでしょうか? 関数に(Javaのように)長い説明的な名前を使用できますが、それでもコードはすぐに管理できなくなります。

オブジェクト指向の方法は、コードを扱いやすいブロックに分割することです。つまり、クラスとオブジェクトおよび関数をオブジェクト インスタンスまたはクラスに関連付けることができます。

クラス関数を使用すると、モジュール レベルの関数と比較して、コード内で別のレベルの分割が得られます。そのため、クラス内で関連する関数をグループ化して、そのクラスに割り当てたタスクにより具体的にすることができます。たとえば、ファイル ユーティリティ クラスを作成できます。

class FileUtil ():
  def copy(source,dest):pass
  def move(source,dest):pass
  def copyDir(source,dest):pass
  def moveDir(source,dest):pass

//usage
FileUtil.copy("1.txt","2.txt")
FileUtil.moveDir("dir1","dir2")

この方法はより柔軟で保守しやすく、関数をグループ化して、各関数が何をするかがより明確になります。また、名前の競合を防ぎます。たとえば、コードで使用する別のインポートされたモジュール (ネットワーク コピーなど) に関数 copy が存在する可能性があるため、完全な名前の FileUtil.copy() を使用すると、問題が削除され、両方のコピー関数が削除されます。並べて使用できます。

于 2011-05-14T10:24:32.273 に答える
7

本音をいうと?staticmethodまたはclassmethodの使用法を見つけたことがありません。グローバル関数やインスタンスメソッドではできない操作はまだ見たことがありません。

PythonがJavaのようにプライベートメンバーと保護されたメンバーを使用した場合は異なります。Javaでは、インスタンスのプライベートメンバーにアクセスして処理を実行できるようにするための静的メソッドが必要です。Pythonでは、それが必要になることはめったにありません。

通常、実際に必要なのはpythonのモジュールレベルの名前空間をより適切に使用することだけであるのに、staticmethodsとclassmethodsを使用している人を目にします。

于 2008-09-01T18:54:29.470 に答える
5

クラスメソッドは、「セマンティックシュガー」(この用語が広く使用されているかどうかはわかりません)または「セマンティックコンビニエンス」を提供します。

例:オブジェクトを表すクラスのセットを取得しました。クラスメソッドを使用するか、all()またはfind()を記述したいUser.all()場合がありUser.find(firstname='Guido')ます。もちろん、モジュールレベルの関数を使用して行うこともできます...

于 2010-08-17T15:53:14.477 に答える
5

以前は PHP を扱っていましたが、最近、このクラスメソッドはどうなっているのだろうかと自問自答していました。Python のマニュアルは非常に技術的で、言葉が非常に短いため、その機能を理解するのに役立ちません。私はグーグルとグーグルで答えを見つけました-> http://code.anjanesh.net/2007/12/python-classmethods.html

あなたがそれをクリックするのが面倒なら。私の説明は短く、以下です。:)

PHP では (PHP を知っている人はあまりいないかもしれませんが、この言語は非常に単純なので、私が話していることを誰もが理解できるはずです)、次のような静的変数があります。


class A
{

    static protected $inner_var = null;

    static public function echoInnerVar()
    {
        echo self::$inner_var."\n";
    }

    static public function setInnerVar($v)
    {
        self::$inner_var = $v;
    }

}

class B extends A
{
}

A::setInnerVar(10);
B::setInnerVar(20);

A::echoInnerVar();
B::echoInnerVar();

どちらの場合も、出力は 20 になります。

ただし、Python では @classmethod デコレータを追加できるため、それぞれ 10 と 20 を出力できます。例:


class A(object):
    inner_var = 0

    @classmethod
    def setInnerVar(cls, value):
        cls.inner_var = value

    @classmethod
    def echoInnerVar(cls):
        print cls.inner_var


class B(A):
    pass


A.setInnerVar(10)
B.setInnerVar(20)

A.echoInnerVar()
B.echoInnerVar()

賢いですね。

于 2010-02-22T13:27:12.047 に答える
2

私は何度か同じ質問を自問していました。そして、ここの人たちは一生懸命説明しようとしましたが、私が見つけた最良の答え (そして最も単純な) 答えは、Python ドキュメントの Class メソッドの説明です。

Static メソッドへの参照もあります。そして、誰かがすでにインスタンスメソッドを知っている場合(私はそう思います)、この答えがすべてをまとめる最後のピースになるかもしれません...

このトピックに関するさらに詳しい説明は、ドキュメントにも記載されています: 標準型階層(インスタンス メソッドセクションまでスクロールダウン)

于 2014-10-14T07:07:55.110 に答える
2

これは興味深いトピックです。私の見解では、python classmethod はファクトリ (生成されたクラスのインスタンスを返す) ではなく、シングルトンのように動作します。それがシングルトンである理由は、生成される共通オブジェクト (辞書) があるが、クラスに対して 1 回だけであり、すべてのインスタンスによって共有されるためです。

これを説明するために、ここに例を示します。すべてのインスタンスが単一のディクショナリへの参照を持っていることに注意してください。私が理解しているように、これはファクトリーパターンではありません。これはおそらく python に非常に固有のものです。

class M():
 @classmethod
 def m(cls, arg):
     print "arg was",  getattr(cls, "arg" , None),
     cls.arg = arg
     print "arg is" , cls.arg

 M.m(1)   # prints arg was None arg is 1
 M.m(2)   # prints arg was 1 arg is 2
 m1 = M()
 m2 = M() 
 m1.m(3)  # prints arg was 2 arg is 3  
 m2.m(4)  # prints arg was 3 arg is 4 << this breaks the factory pattern theory.
 M.m(5)   # prints arg was 4 arg is 5
于 2012-09-07T05:06:16.973 に答える
0

もちろん、クラスは一連のインスタンスを定義します。また、クラスのメソッドは個々のインスタンスで機能します。クラス メソッド (および変数) は、インスタンス セット全体に関連する他の情報を格納する場所です。

たとえば、クラスが学生のセットを定義する場合、学生がメンバーになることができるグレードのセットなどを定義するクラス変数またはメソッドが必要になる場合があります。

クラス メソッドを使用して、セット全体で作業するためのツールを定義することもできます。たとえば、Student.all_of_em() は既知のすべての学生を返す場合があります。インスタンスのセットが単なるセットよりも多くの構造を持っている場合は、明らかに、その構造について知るクラス メソッドを提供できます。Students.all_of_em(grade='ジュニア')

このような手法は、インスタンスのセットのメンバーを、クラス変数をルートとするデータ構造に格納する傾向があります。その場合、ガベージ コレクションをイライラさせないように注意する必要があります。

于 2016-09-08T01:29:19.767 に答える