Python - 函数内类对象初始化
下面的代码可以正常运行(最终会通过SQL从数据库中提取记录),但我不太明白下面代码中的assert
语句为什么能正常工作。
def build_row(table, cols):
"""Build a class that creates instances of specific rows"""
class DataRow:
"""Generic data row class, specialized by surrounding function"""
def __init__(self, data):
"""Uses data and column names to inject attributes"""
assert len(data)==len(self.cols)
for colname, dat in zip(self.cols, data):
setattr(self, colname, dat)
def __repr__(self):
return "{0}_record({1})".format(self.table, ", ".join(["{0!r}".format(getattr(self, c)) for c in self.cols]))
DataRow.table = table
DataRow.cols = cols.split()
return DataRow
‘data’ 的长度是什么时候确定的?它是怎么保证和 ‘self.cols’ 的长度一样的?能不能有人解释一下这个行为?
3 个回答
当你定义了 DataRow
这个类时,列名是存放在一个叫 self.cols
的变量里的。所以,当你创建这个类的实例时,你需要为每一列都填上数据。
这就是为什么你需要两个列表的长度要一样。如果长度不一样,你可能就没有填满所有的属性。
现在的做法是用列名来设置属性,这样做并不是最好的选择,因为如果你的列名恰好是 cols
、table
,甚至是 __repr__
,那么你的代码可能就会出问题。
当你使用 build_row()
函数创建这个类的时候,data
会被赋一个值。
但是要注意,data
的长度并不一定和 self.cols
一样;可能你根本没有创建这个类,所以也就不会看到错误。
如果你把代码中的 self.cols
和 self.table
替换成 self.__class__.cols
和 self.__class__.table
,可能会更容易理解。这种困惑是因为通过 self
对象访问类属性时,看起来像是在访问实例属性,这也是我不太喜欢这种写法的原因。当你在 __init__
这个地方查看代码时,通常会给实例属性赋值,但突然看到一行代码在读取 self.cols
的值时,会让人感到奇怪:那 self.cols
是从哪里来的呢?另一方面,如果你把这个断言改成:
assert len(data)==len(self.__class__.cols)
那么就更清楚了,数据值的列表应该和这个 DataRow 实例定义的列列表长度一致。类属性 cols
在类定义后立即被初始化,具体是在这行代码:
DataRow.cols = cols.split()
在任何实例被创建之前就已经初始化了——这就是为什么这个断言能正常工作的原因。
写这段代码的人可能会考虑改用更现代的 namedtuple
结构。