Python组合setattr和getattr

1 投票
2 回答
4610 浏览
提问于 2025-04-17 18:24

我想动态更新一个类的属性,但似乎用setattr和getattr的组合并不能达到我想要的效果。

这是我的主要类:

class Container(object):
    def __init__(self):
        pass
container = Container()
attributes = ['a', 'b', 'c', 'd']
values = [[1, 2, 3, 4, 5], [True, False], ['red', 'blue', 'green'], [0, 1, -1, -5, 99]]

请注意,为了这个例子,我明确列出了属性及其对应的值。然而,在实际应用中,我并不知道这些属性的数量、名称或值。这就需要我动态处理这些内容。

这是代码的其余部分:

for key, value in zip(attributes, values):
    setattr(container, key, [])
    for val in value:
        setattr(container, key, getattr(container, key).append(val))

当我运行代码时,这部分并不工作。我可以把getattr的结果存到一个临时变量里,然后在调用setattr之前先对列表调用append方法,但我希望能尽量简化这个过程。

有人能告诉我为什么这样不行吗?我还有哪些其他选择呢?

谢谢你的帮助。

2 个回答

1

如果你能看到每一步的进展,可能会更容易理解发生了什么:

class Container(object):
    def __init__(self):
        pass
    def __str__(self):
        return '%s(%s)' % (self.__class__.__name__,
            ', '.join(['%s = %s' % (attr, getattr(self, attr))
                for attr in self.__dict__]))

现在你可以用 print container 来查看内容。如果你在嵌套循环中这样做,你会发现第一次尝试使用 setattr 后,原本存储在 container.a 里的列表被覆盖了(正如Martijn所提到的):

for key, value in zip(attributes, values):
    setattr(container, key, [])
    for val in value:
        print 'before:', container
        setattr(container, key, getattr(container, key).append(val))
        print 'after:', container

before: Container(a = [])
after: Container(a = None)
before: Container(a = None)
Traceback (most recent call last): ...

所谓的“最佳”做法显然取决于实际问题——考虑到缩小后的问题,Martijn的版本是相当合适的,但也许在创建初始的 container 实例后,你需要在某个时刻添加一个项目,或者添加多个项目。

3

你正在直接修改一个列表。像所有直接修改的函数一样,.append() 返回的是 None,所以你最后一行代码:

setattr(container, key, getattr(container, key).append(val))

最终的结果是:

setattr(container, key, None)

只需在类中设置一个列表的副本:

for key, value in zip(attributes, values):
    setattr(container, key, values[:])

这里的 [:] 是通过切片从 0 到 len(values) 来创建 values 的副本,这样就不需要用循环了。

如果你只想创建一个对象,让它像 dict 一样提供键和值作为属性(而不是通过索引访问),你也可以使用:

class Container(object):
    def __init__(self, names, values):
        self.__dict__.update(zip(names, values))

然后运行:

Container(attributes, values)

这样就不需要在循环中调用 setattr() 了。

撰写回答