web-dev-qa-db-ja.com

クラスの属性を事前に初期化する方が良いですか、それとも途中で追加する方が良いですか?

これが絶対にソフォモリックな質問である場合は申し訳ありませんが、ベストプラクティスがどこにあるのか知りたいので、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]

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

11
Gilead

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

あなたはしません。柔軟性が問題の原因です。コードがオブジェクトの属性を変更する可能性がある場合、保守性はすでにバラバラです。理想的には、すべてのクラスに、__init__の後にすべてのインスタンスで同じ属性が設定されています。常に可能または賢明であるとは限りませんが、それを回避するためのreally good理由がない場合は常に当てはまります。

1つのアイデアは、遭遇すると予想されるすべての属性でクラスを事前初期化することです

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

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

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

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

10
user7043

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

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])

そうすれば、少なくとも読者にとって、dataは単なるデータストアであり、どの値がどの名前で保存されているかをすぐに確認できるので、それはすべて1行で行われます。

そして、はい、このように物事を行うことは、実際には良い考えです。

7
pillmuncher

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

可能ですprocessDataはメソッド(process_dataはクラスに作用するため、python命名規則)に従います。この例では、データ構造(dictで十分な場合があります)のほうが適しているようです。

実際の例を考えると、質問を CodeReview に取り、コードのリファクタリングに役立つ可能性があります。

1
Casey Kuball