19

Pythonで静的メソッドが使用されるのはいつ、どのように想定されていますか? クラスメソッドをファクトリメソッドとして使用してオブジェクトのインスタンスを作成することは、可能な限り避けるべきであることをすでに確立しました。つまり、クラス メソッドを代替コンストラクタとして使用することはベスト プラクティスではありません ( Python オブジェクトのファクトリ メソッド - ベスト プラクティス を参照)。

データベース内のエンティティ データを表すために使用されるクラスがあるとします。データがdictフィールド名とフィールド値を含むオブジェクトであり、フィールドの 1 つがデータを一意にする ID 番号であるとします。

class Entity(object):
    def __init__(self, data, db_connection):
        self._data = data
        self._db_connection

ここで、__init__メソッドはエンティティ データdictオブジェクトを受け取ります。EntityID 番号しか持っていないので、インスタンスを作成したいとします。まず、残りのデータを見つけてから、Entityオブジェクトのインスタンスを作成する必要があります。以前の質問から、クラス メソッドをファクトリ メソッドとして使用することは、可能な限り避けるべきであることがわかりました。

class Entity(object):

    @classmethod
    def from_id(cls, id_number, db_connection):
        filters = [['id', 'is', id_number]]
        data = db_connection.find(filters)
        return cls(data, db_connection)

    def __init__(self, data, db_connection):
        self._data = data
        self._db_connection


# Create entity
entity = Entity.from_id(id_number, db_connection)

上記は、してはいけないこと、または代替手段がある場合に少なくともしてはいけないことの例です。クラスメソッドを編集して、ユーティリティメソッドに近づけ、ファクトリメソッドを減らすことが有効な解決策であるかどうか疑問に思っています。つまり、次の例は、静的メソッドを使用するためのベスト プラクティスに準拠していますか。

class Entity(object):

    @staticmethod
    def data_from_id(id_number, db_connection):
        filters = [['id', 'is', id_number]]
        data = db_connection.find(filters)
        return data


# Create entity
data = Entity.data_from_id(id_number, db_connection)
entity = Entity(data)

それとも、スタンドアロン関数を使用して ID 番号からエンティティ データを検索する方が理にかなっているでしょうか。

def find_data_from_id(id_number, db_connection):
    filters = [['id', 'is', id_number]]
    data = db_connection.find(filters)
    return data


# Create entity.
data = find_data_from_id(id_number, db_connection)
entity = Entity(data, db_connection)

__init__注:メソッドを変更したくありません。以前は、私の__init__メソッドを次のようにすることを提案して__init__(self, data=None, id_number=None)いましたが、エンティティ データを見つけるには 101 通りの方法がある可能性があるため、そのロジックをある程度分けておくことをお勧めします。わかる?

4

4 に答える 4

30

Pythonで静的メソッドが使用されるのはいつ、どのように想定されていますか?

グリブの答えは次のとおりです。あまり頻繁ではありません。

平凡ですが、それほど役に立たない答えは次のとおりです。コードが読みやすくなったとき。


まず、ドキュメントに寄り道しましょう:

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

つまり、C++ で静的メソッドが必要な場合は、Python で静的メソッドが必要ですよね?

うーん、ダメ。

Java には関数がなく、メソッドだけがあるため、静的メソッドの単なるバンドルである疑似クラスを作成することになります。Python で同じことを行う方法は、無料の関数を使用することです。

それは明らかです。ただし、適切なクラスが関数を割り込むのをできる限り難しく探すのは良い Java スタイルです。そのため、これらの疑似クラスの記述を避けることができますが、同じことをすることは悪い Python スタイルです (ここでも無料の関数を使用します)。あまり明白ではありません。

C++ には Java と同じ制限はありませんが、多くの C++ スタイルはいずれにしてもかなり似ています。(一方、「フリー関数はクラスのインターフェイスの一部である」というイディオムを内面化した「モダン C++」プログラマーの場合、「静的メソッドはどこで役立つか」に対する直感は、おそらく Python ではかなり適切です。)


しかし、他の言語からではなく、第一原理からこれを考えているのであれば、もっと簡単に物事を見る方法があります。

A@staticmethodは基本的に単なるグローバル関数です。foo_module.bar()と綴られた方が何らかの理由でより読みやすい関数がある場合はfoo_module.BazClass.bar()、それを@staticmethod. そうでない場合は、しないでください。本当にそれだけです。唯一の問題は、慣れ親しんだ Python プログラマーにとってより読みやすいものを求める本能を構築することです。

もちろん@classmethod、インスタンスではなくクラスへのアクセスが必要な場合は a を使用します。ドキュメントが示唆しているように、代替コンストラクターはそのパラダイムケースです。クラスを明示的に参照するだけで a をシミュレートできることがよくありますが(特にサブクラス化があまりない場合)、そうすべきではありません。@classmethod@staticmethod


最後に、特定の質問に到達します。

クライアントが ID でデータを検索する必要がある唯一の理由が を構築するEntityことである場合、それは公開すべきではない実装の詳細のように思えます。また、クライアント コードがより複雑になります。コンストラクタを使用するだけです。あなたがあなたを変更したくない場合__init__(そして、あなたがしたくないかもしれない正当な理由があることは正しいです)、@classmethod代替コンストラクタとして a を使用してください:Entity.from_id(id_number, db_connection).

一方、そのルックアップが、Entity構築とは何の関係もない他のケースでクライアントにとって本質的に有用なものである場合、これはクラスとは何の関係もないように見えますEntity(または、少なくとも同じモジュール内の他の何よりも)。だから、それを無料の機能にしてください。

于 2013-02-22T19:42:24.100 に答える
11

リンクされた質問への回答は、具体的には次のように述べています。

@classmethod は、「代替コンストラクター」を実行するための慣用的な方法です。stdlib のいたるところに例があります。itertools.chain.from_iterable、datetime.datetime.fromordinal などです。

したがって、クラスメソッドの使用が本質的に悪いという考えをどのようにして得たのかわかりません。コードをたどり、APIを簡単に使用できるようになるため、特定の状況でクラスメソッドを使用するというアイデアが実際に気に入っています。

別の方法は、次のようにデフォルトのコンストラクター引数を使用することです。

class Entity(object):
    def __init__(self, id, db_connection, data=None):
        self.id = id
        self.db_connection = db_connection
        if data is None:
            self.data = self.from_id(id, db_connection)
        else:
            self.data = data

    def from_id(cls, id_number, db_connection):
        filters = [['id', 'is', id_number]]
        return db_connection.find(filters)

しかし、私はあなたが最初に書いたクラスメソッドのバージョンを好みます。特にそれ以来、dataかなりあいまいです。

于 2013-02-22T06:08:28.847 に答える
9

あなたの最初の例は私にとって最も理にかなっていますEntity.from_id。かなり簡潔で明確です。

data次の 2 つの例では、返される内容を説明していないの使用を避けています。はdataを構築するために使用されEntityます。dataを構築するために使用されるものについて具体的にしたい場合は、メソッドにまたは同等の関数のEntityような名前を付けることができます。Entity.with_data_for_identity_with_data_for_id

などの動詞を使用するfindと、戻り値を示すものがないため、かなり混乱する可能性があります — データが見つかったときに関数は何をすべきでしょうか? (はい、私はメソッドstrがあることを認識していfindます; という名前の方が適切ではないでしょうindex_ofか? しかし、それからもありindexます ...) それは私に古典を思い出させます:

×を見つける

私は常に、(a) システムの知識がなく、(b) システムの他の部分の知識がある人にとって、名前が何を示すかを考えようとします — 私が常に成功しているとは言いません!

于 2013-02-22T06:08:18.767 に答える