8

質問:__init__コレクションの内容をアンパックするのではなく、コレクションを引数として直接取るan を作成することの長所と短所は何 ですか?

コンテキスト:データベース テーブルの複数のフィールドからのデータを処理するクラスを作成しています。大規模な (~1 億行) クエリ結果を反復処理し、一度に 1 行ずつ処理を実行するクラスに渡します。各行はデータベースからタプル (またはオプションで辞書) として取得されます。

ディスカッション:正確に 3 つのフィールドに関心があると仮定しますが、クラスに渡されるものはクエリによって異なり、クエリはユーザーによって記述されます。最も基本的なアプローチは、次のいずれかです。

class Direct:
    def __init__(self, names):
        self.names = names

class Simple:
    def __init__(self, names):
        self.name1 = names[0]
        self.name2 = names[1]
        self.name3 = names[2]

class Unpack:
    def __init__(self, names):
        self.name1, self.name2, self.name3 = names

新しいインスタンスに渡される可能性のある行の例を次に示します。

good = ('Simon', 'Marie', 'Kent')                 # Exactly what we want
bad1 = ('Simon', 'Marie', 'Kent', '10 Main St')   # Extra field(s) behind
bad2 = ('15', 'Simon', 'Marie', 'Kent')           # Extra field(s) in front
bad3 = ('Simon', 'Marie')                         # Forgot a field

上記に直面すると、Direct常に実行されます (少なくともこの時点まで) が、バグが発生する可能性が非常に高くなります (GIGO)。1 つの引数を取り、与えられたとおりに割り当てます。したがって、これは任意のサイズのタプルまたはリスト、Null 値、関数参照などになる可能性があります。オブジェクトですが、明らかに処理するように設計されていないデータを与えると、クラスはすぐに文句を言うべきだと思います。

Simplebad1は正しく処理され、 が指定されるとバグbad2が発生し、 が指定されるとエラーがスローされますbad3。からの入力を効果的に切り捨てることができるのは便利ですが、bad1から来るバグに値するものではありませんbad2。これは素朴で矛盾しているように感じます。

Unpack3つの「悪い」ケースすべてでエラーがスローされるため、最も安全なアプローチのようです。データベースに悪い情報を黙って入力するのは、絶対に避けたいことですよね? タプルを直接取得しますが、インデックスを参照し続けることを強制する代わりに、その内容を個別の属性として識別できるようにし、タプルのサイズが間違っている場合は文句を言います。

一方で、なぜコレクションを渡すのでしょうか。常に 3 つのフィールドが必要であることはわかっているので、明示的に 3 つの引数を受け入れるように定義__init__し、新しいオブジェクトに渡すときに *-operator を使用してコレクションをアンパックできます。

class Explicit:
    def __init__(self, name1, name2, name3):
        self.name1 = name1
        self.name2 = name2
        self.name3 = name3

names = ('Guy', 'Rose', 'Deb')
e = Explicit(*names)

私が見る唯一の違いは、__init__定義がもう少し冗長であり、タプルが間違ったサイズTypeErrorの場合の代わりにレイズすることです。ValueError哲学的には、データのグループ (クエリの行) を取得してその部分 (3 つのフィールド) を調べる場合、データのグループ (タプル) を渡し、その部分 (3 つの属性)。それでUnpack良いでしょう。

常に 3 つではなく不確定な数のフィールドを受け入れたい場合でも、タプルを直接渡すか、任意の引数リスト (*args、**kwargs) と*-operator アンパックを使用するかを選択できます。だから、これは完全にニュートラルなスタイルの決定なのだろうか?

4

1 に答える 1

5

この質問は、さまざまなアプローチを試して、自分にとって何が最も理にかなっていて、コードを読んでいる他の人が最も簡単に理解できるかを確認することによって、おそらく最もよく答えられます。

より多くの経験を積むことができた今、これらの価値にどのようにアクセスするつもりなのか自問したいと思います。

このコレクションのいずれかの値にアクセスする場合、同じサブルーチンまたはコードのセクションでほとんどまたはすべての値を使用する可能性がありますか? もしそうなら、「直接」アプローチは良い選択です。それは最もコンパクトであり、コレクションをコレクションとして考えさせてくれるので、中身に絶対に注意を払う必要があります。

一方、ここでいくつかの値を使用している場合、値を直接参照するだけでよいのに、アクセスするインデックスを常に覚えたり、辞書キーの形式で冗長性を追加したりする必要はありません。個別に名前が付けられた属性を使用します。この場合、クラスが最初に初期化されたときにコレクションが存在するという事実について考えるだけでよいように、おそらく「直接」アプローチを避けるでしょう。

残りの各アプローチには、コレクションをさまざまな属性に分割することが含まれます。ここで明確な勝者は「明示的な」アプローチだと思います。「シンプル」と「アンパック」のアプローチは、コレクションの順序に対する隠れた依存関係を共有していますが、本当の利点はありません。

于 2014-12-15T23:30:45.907 に答える