【问题标题】:Python combining setattr and getattrPython 结合 setattr 和 getattr
【发布时间】:2013-03-08 11:32:30
【问题描述】:

我想动态更新一个类的属性,但是 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 方法,但如果可能的话,我想压缩它。

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

感谢您的帮助

【问题讨论】:

  • "这部分在我运行代码时不起作用"怎么办?另外,您预计会发生什么?

标签: python class getattr setattr


【解决方案1】:

您正在就地附加到列表。像所有就地突变函数一样,.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() 的需要。

【讨论】:

  • 感谢您的澄清。你知道为什么 getattr(container, key, value) 似乎和 setattr(container, key, value) 一样工作吗(一旦属性之前设置过一次)?
  • @user1713952:是getattr(container, key, default);如果container 上没有设置属性key,则返回default。既然你已经设置了key,getattr(container, key)就足够了。
  • @JonClements:为什么defaultdict() 会在这里提供帮助?替换Container 你的意思是?整个追加循环是一个红鲱鱼,没有组合的列表,只有与现有列表配对的键。
  • @Martijn 我还没有 100% 清醒,但看起来 Container 可能只是那样......
  • 是的,我想用 dict 代替。但是我需要在这些对象上运行一些特定的方法,并且在类中定义它们更方便。另外,属性访问的语法对类更紧凑不是吗?
【解决方案2】:

如果您可以观察每个步骤的进度,它可能会帮助您了解发生了什么:

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 实例。

【讨论】:

    猜你喜欢
    • 2014-04-05
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 2011-10-17
    • 1970-01-01
    • 1970-01-01
    • 2015-09-19
    • 1970-01-01
    相关资源
    最近更新 更多