84

私は、Pythonのオンとオフをいじくり回しているJava開発者です。私は最近、JavaプログラマーがPythonを手にしたときによくある間違いについて言及しているこの記事に出くわしました。最初のものが私の目に留まりました:

Javaの静的メソッドは、Pythonクラスメソッドに変換されません。確かに、ほぼ同じ効果が得られますが、クラスメソッドの目標は、実際にはJavaでは通常は不可能なこと(デフォルト以外のコンストラクターを継承するなど)を実行することです。Java静的メソッドの慣用的な変換は通常、クラスメソッドや静的メソッドではなく、モジュールレベルの関数です。(静的なfinalフィールドは、モジュールレベルの定数に変換する必要があります。)

これはパフォーマンスの問題ではありませんが、このようなJavaイディオムコードを使用する必要があるPythonプログラマーは、Foo.someFunctionである必要があるときにFoo.Foo.someMethodと入力するとかなりイライラします。ただし、classmethodの呼び出しには、staticmethodまたは関数の呼び出しには含まれない追加のメモリ割り当てが含まれることに注意してください。

ああ、そしてそれらすべてのFoo.Bar.Baz属性チェーンも無料ではありません。Javaでは、これらの点線の名前はコンパイラーによって検索されるため、実行時に、それらの名前がいくつあるかは実際には関係ありません。Pythonでは、ルックアップは実行時に行われるため、各ドットがカウントされます。(Pythonでは、「フラットはネストよりも優れている」ことを覚えておいてください。ただし、パフォーマンスよりも「読みやすさのカウント」と「シンプルは複雑よりも優れています」に関連しています。)

staticmethodのドキュメントには次のように書かれているので、これは少し奇妙だと思いました。

Pythonの静的メソッドは、JavaまたはC++で見られるものと似ています。代替クラスコンストラクターの作成に役立つバリアントについては、classmethod()も参照してください。

さらに不可解なのは、このコードです。

class A:
    def foo(x):
        print(x)
A.foo(5)

Python 2.7.3では期待どおりに失敗しますが、3.2.3では正常に機能します(ただし、Aのインスタンスでメソッドを呼び出すことはできず、クラスでのみ呼び出すことができます)。

したがって、静的メソッドを実装する方法は3つあり(classmethodを使用してカウントする場合は4つ)、それぞれ微妙な違いがあり、そのうちの1つは文書化されていないようです。これは、Pythonのマントラである「1つ、できれば1つだけ」の明白な方法とは相容れないようです。最もPythonicなイディオムはどれですか?それぞれの長所と短所は何ですか?

これまで私が理解していることは次のとおりです。

モジュール機能:

  • Foo.Foo.f()の問題を回避します
  • モジュールの名前空間を他の方法よりも汚染します
  • 継承なし

staticmethod:

  • クラスに関連する関数をクラス内およびモジュール名前空間外に保持します。
  • クラスのインスタンスで関数を呼び出すことができます。
  • サブクラスはメソッドをオーバーライドできます。

classmethod:

  • staticmethodと同じですが、最初の引数としてクラスを渡します。

通常の方法(Python 3のみ)

  • staticmethodと同じですが、クラスのインスタンスでメソッドを呼び出すことはできません。

私はこれを考えすぎていますか?これは問題ではありませんか?助けてください!

4

4 に答える 4

113

それについて考える最も簡単な方法は、メソッドがその作業を行うために必要なオブジェクトのタイプの観点から考えることです。メソッドがインスタンスにアクセスする必要がある場合は、それを通常のメソッドにします。クラスにアクセスする必要がある場合は、クラスメソッドにします。クラスやインスタンスにアクセスする必要がない場合は、関数にします。静的メソッドを作成する必要はめったにありませんが、クラスにアクセスする必要がない場合でも、関数をクラスと「グループ化」したい場合(たとえば、オーバーライドできるようにする場合)、私は推測しますあなたはそれをstaticmethodにすることができます。

モジュールレベルで関数を配置しても、名前空間が「汚染」されないことを付け加えておきます。関数が使用されることを意図している場合、それらは名前空間を汚染しているのではなく、使用されるべきであるようにそれを使用しています。関数は、クラスなどと同じように、モジュール内の正当なオブジェクトです。そこに存在する理由がない場合、クラス内の関数を非表示にする理由はありません。

于 2012-08-03T02:01:32.300 に答える
37

BrenBarnによる素晴らしい回答ですが、「クラスまたはインスタンスへのアクセスが必要ない場合は、関数にする」を次のように変更します。

'クラスまたはインスタンスへのアクセスは必要ないが、テーマ的にクラスに関連している場合 (典型的な例: 他のクラス メソッドまたは代替コンストラクターによって使用されるヘルパー関数および変換関数)、次にstaticmethodを使用します。

それ以外の場合はモジュール関数にします

于 2013-04-18T10:02:31.863 に答える