Python - 函数内类对象初始化

0 投票
3 回答
2370 浏览
提问于 2025-04-16 19:26

下面的代码可以正常运行(最终会通过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 个回答

0

当你定义了 DataRow 这个类时,列名是存放在一个叫 self.cols 的变量里的。所以,当你创建这个类的实例时,你需要为每一列都填上数据。

这就是为什么你需要两个列表的长度要一样。如果长度不一样,你可能就没有填满所有的属性。

现在的做法是用列名来设置属性,这样做并不是最好的选择,因为如果你的列名恰好是 colstable,甚至是 __repr__,那么你的代码可能就会出问题。

1

当你使用 build_row() 函数创建这个类的时候,data 会被赋一个值。

但是要注意,data 的长度并不一定和 self.cols 一样;可能你根本没有创建这个类,所以也就不会看到错误。

1

如果你把代码中的 self.colsself.table 替换成 self.__class__.colsself.__class__.table,可能会更容易理解。这种困惑是因为通过 self 对象访问类属性时,看起来像是在访问实例属性,这也是我不太喜欢这种写法的原因。当你在 __init__ 这个地方查看代码时,通常会给实例属性赋值,但突然看到一行代码在读取 self.cols 的值时,会让人感到奇怪:self.cols 是从哪里来的呢?另一方面,如果你把这个断言改成:

assert len(data)==len(self.__class__.cols)

那么就更清楚了,数据值的列表应该和这个 DataRow 实例定义的列列表长度一致。类属性 cols 在类定义后立即被初始化,具体是在这行代码:

DataRow.cols = cols.split()

在任何实例被创建之前就已经初始化了——这就是为什么这个断言能正常工作的原因。

写这段代码的人可能会考虑改用更现代的 namedtuple 结构。

撰写回答