关于类:结合setattr和getattr的Python

Python combining setattr and getattr

我想动态地更新类的属性,但似乎setattr和getattr的组合并不能像我想使用的那样工作。

这是我的主要课程:

1
2
3
4
5
6
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]]

请注意,为了这个示例的目的,我显式地构造了属性列表及其各自的值。然而,在这个代码的实际应用中,我并没有提前知道任何事情。它们的数字、名称或值都不是。这就需要动态地进行。

下面是代码的其余部分:

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

运行代码时,此部分不起作用。我可以将getattr部分保存在tmp变量中,然后在调用setattr之前为列表调用append方法,但如果可能的话,我想对其进行压缩。

有人能解释我为什么这不起作用吗?我还有什么选择?

谢谢你的帮助


您正在将添加到适当的列表中。像所有的就地突变函数一样,.append()返回None,所以最后一行:

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

最终评估为:

1
setattr(container, key, None)

只需在类上设置列表的副本:

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

其中,[:]通过将0分割为len(values)的形式,以一个简短的手写符号创建values的副本,而不需要循环。

如果您只想创建一个提供键和值作为属性的对象(就像dict将具有属性访问而不是项访问),您还可以使用:

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

然后运行:

1
Container(attributes, values)

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


如果你能观察每一步的进展,它可能会帮助你理解正在发生的事情:

1
2
3
4
5
6
7
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__]))

现在你可以开始了。如果您在嵌套循环中这样做,您将看到在第一次尝试setattr之后,您已经删除了存储在container.a中的原始列表(如martijn所述):

1
2
3
4
5
6
7
8
9
10
11
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实例之后的某个时刻,您可能需要附加一个项目,或者使用多个项目进行扩展。