我想动态更新类的属性,但似乎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部分保存在tmp变量中,然后在调用setattr之前调用列表的append方法,但我想在可能的情况下压缩它。
任何人都可以解释为什么这不起作用?我有什么选择?
感谢您的帮助
答案 0 :(得分: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切换到values
而不需要循环来创建len(values)
的副本。
如果您只想创建一个将键和值作为属性提供的对象(很像dict
还有属性访问而不是项目访问权限),您还可以使用:
class Container(object):
def __init__(self, names, values):
self.__dict__.update(zip(names, values))
然后运行:
Container(attributes, values)
,无需在循环中调用setattr()
。
答案 1 :(得分: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
实例。