问题:__init__
编写直接将集合作为参数而不是解包其内容的优点和缺点是 什么?
上下文:我正在编写一个类来处理来自数据库表中多个字段的数据。我遍历一些大型(约 1 亿行)查询结果,一次将一行传递给执行处理的类。每行都作为元组(或可选地,作为字典)从数据库中检索。
讨论:假设我对三个字段感兴趣,但是传递给我的类的内容取决于查询,并且查询是由用户编写的。最基本的方法可能是以下之一:
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)。它接受一个参数并完全按照给定的方式分配它,因此这可以是一个元组或任何大小的列表、一个 Null 值、一个函数引用等。这是我能想到的最快速和最肮脏的初始化方法对象,但我觉得当我给它显然不是为了处理而设计的数据时,该类应该立即抱怨。
Simple
bad1
正确处理,在给定时是错误bad2
的,并且在给定时抛出错误bad3
。能够有效地截断输入很方便,bad1
但不值得出现错误bad2
. 这个感觉很幼稚和不一致。
Unpack
似乎是最安全的方法,因为它在所有三个“坏”情况下都会引发错误。我们要做的最后一件事就是默默地用不良信息填充我们的数据库,对吧?它直接采用元组,但允许我将其内容识别为不同的属性,而不是强迫我继续引用索引,并抱怨元组的大小是否错误。
另一方面,为什么要传递一个集合呢?因为我知道我总是想要三个字段,所以我可以定义__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
当元组大小错误时。从哲学上讲,如果我们获取一组数据(查询的一行)并检查其部分(三个字段),我们应该传递一组数据(元组)但存储其部分(三个属性)。所以Unpack
会更好。
如果我想接受不确定数量的字段,而不是总是三个,我仍然可以选择直接传递元组或使用任意参数列表(*args、**kwargs)和*
-operator 解包。所以我想知道,这是一个完全中性的风格决定吗?