質問:__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 値、関数参照などになる可能性があります。オブジェクトですが、明らかに処理するように設計されていないデータを与えると、クラスはすぐに文句を言うべきだと思います。
Simple
bad1
は正しく処理され、 が指定されるとバグbad2
が発生し、 が指定されるとエラーがスローされますbad3
。からの入力を効果的に切り捨てることができるのは便利ですが、bad1
から来るバグに値するものではありませんbad2
。これは素朴で矛盾しているように感じます。
Unpack
3つの「悪い」ケースすべてでエラーがスローされるため、最も安全なアプローチのようです。データベースに悪い情報を黙って入力するのは、絶対に避けたいことですよね? タプルを直接取得しますが、インデックスを参照し続けることを強制する代わりに、その内容を個別の属性として識別できるようにし、タプルのサイズが間違っている場合は文句を言います。
一方で、なぜコレクションを渡すのでしょうか。常に 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 アンパックを使用するかを選択できます。だから、これは完全にニュートラルなスタイルの決定なのだろうか?