クラス内の属性を事前に初期化することをお勧めしますか、それとも途中で追加することですか?


12

これが絶対に二日酔いの質問である場合は申し訳ありませんが、ベストプラクティスがどこにあるのか興味があり、Googleで良い答えを見つけることができないようです。

Pythonでは、通常、空のクラスをスーパーキャッチオールデータ構造コンテナー(JSONファイルのようなもの)として使用し、途中で属性を追加します。

class DataObj:
    "Catch-all data object"
    def __init__(self):
        pass

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

これにより、コンテナオブジェクトは基本的に何でも保存できるため、非常に大きな柔軟性が得られます。したがって、新しい要件が生じた場合は、DataObjオブジェクトに別の属性として追加します(コードで渡します)。

しかし、最近、コードの読み取りが非常に難しくなるため、これはひどい慣行であると私(FPプログラマー)に感銘を受けました。すべてのコードを調べて、DataObjが実際に持っている属性を把握する必要があります。

質問:柔軟性を犠牲にすることなく保守性を高めるために、これをどのように書き直すことができますか?

関数型プログラミングから採用できるアイデアはありますか?

私はそこにベストプラクティスを探しています。

:1つの考えは、遭遇することが予想されるすべての属性でクラスを事前初期化することです。例えば

class DataObj:
    "Catch-all data object"
    def __init__(self):
        data.a = 0
        data.b = ""
        data.c = []

def processData(inputs):
    data = DataObj()
    data.a = 1
    data.b = "sym"
    data.c = [2,5,2,1]

これは実際には良いアイデアですか?自分の属性がアプリオリであることがわからない場合はどうなりますか?


データ構造は非常に可変であるため、保守性に懸念があるようです。豊富な空き時間™で、不変データモデルに関するこの記事をお読みください。それはあなたがデータについて考える理由を完全に変えるかもしれません。
9000

@ 9000そのような記事は、すでに確信している人を再説得します。私にとっては、理由よりもハウツーのように思えました(理由のリストは、特定のニーズがあると感じない限り、本当に説得力がありません)。私にとっては、VBで請求書を更新する人に、請求書オブジェクトの新しいコピーを常に作成しなければならないことが理にかなっている(納税、新しい請求書オブジェクトの追加、部品、新しい請求書オブジェクトの追加)ことを納得させません。
ポール

回答:


10

柔軟性を犠牲にすることなく、保守性を高めるためにこれをどのように書き直すことができますか?

あなたはしません。柔軟性がまさに問題の原因です。どこかのコードがオブジェクトの属性を変更する可能性がある場合、保守性はすでに細分化されています。理想的には、すべてのクラスには、__init__すべてのインスタンスで同じように設定される一連の属性があります。常に可能または賢明というわけではありませんが、それを避けるための本当に正当な理由がない場合は常にそうすべきです。

一つの考えは、遭遇することを期待するすべての属性でクラスを事前に初期化することです

それは良い考えではありません。確かに、属性はそこにありますが、偽の値、または値を割り当てていないコードをカバーする有効な値(またはつづりの間違った値)を持つ場合もあります。AttributeError怖いですが、間違った結果を得るのはもっと悪いです。一般にデフォルト値で問題ありませんが、適切なデフォルトを選択する(および必要なものを決定する)には、オブジェクトの用途を知る必要があります。

自分の属性がアプリオリであることがわからない場合はどうなりますか?

その後、いずれの場合もめちゃくちゃになり、属性名をハードコーディングする代わりに辞書またはリストを使用する必要があります。しかし、私はあなたが「...コンテナクラスを書くとき」を意味すると考えています。その答えは、「ロックステップでファイルを編集できますよ」と。新しい属性が必要ですか?フリギング属性をコンテナクラスに追加します。そのクラスを使用するコードがさらにあり、その属性は必要ありませんか?物事を2つの別々のクラスに分割することを検討してください(ミックスインを使用してDRYを維持します)。

繰り返しコンテナクラスを作成するのが怖い場合:メタプログラミングを慎重に適用するcollections.namedtupleか、作成後にメンバーを変更する必要がない場合に使用します(FPメンバーは喜んでいます)。


7

Alex MartelliのBunchクラスをいつでも使用できます。あなたの場合:

class DataObj:
    "Catch-all data object"
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

def processData(inputs):
    data = DataObj(a=1, b="sym", c=[2,5,2,1])

そうすれば、少なくともデータは単なるデータストアであり、すべての行で発生するため、どの値がどの名前で格納されているかをすぐに確認できます。

そして、はい、このように物事を行うことは、実際には時々良いアイデアです。


1

おそらく、None無効なデータを示すために使用する2番目のアプローチを使用します。後で属性を追加すると、読み取り/保守が困難になるのは事実です。ただし、このクラス/オブジェクトの目的に関する詳細情報は、最初のアイデアが悪い設計である理由についての洞察を提供します。メソッドまたはデフォルトデータのない完全に空のクラスはどこにありますか? クラスにどの属性があるのか​​わからないのはなぜですか?

それはだ可能そのprocessData方法は(と良いかもしれないprocess_data、それはクラスに作用するので、pythonの命名規則に従うこと)。この例を考えると、データ構造としては良いかもしれません(aでdict十分かもしれません)。

実際の例を考えれば、質問をCodeReviewに持ち込んで、コードのリファクタリングに役立てることができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.