【问题标题】:Dynamically get dict elements via getattr?通过 getattr 动态获取 dict 元素?
【发布时间】:2012-02-01 18:19:11
【问题描述】:

我想动态查询我想检索的类中的哪些对象。 getattr 似乎是我想要的,它对类中的顶级对象执行良好。但是,我还想指定子元素。

class MyObj(object):
    def __init__(self):
        self.d = {'a':1, 'b':2}
        self.c = 3

myobj = MyObj()
val = getattr(myobj, "c")
print val # Correctly prints 3
val = getattr(myobj, "d['a']") # Seemingly incorrectly formatted query
print val # Throws an AttributeError

如何通过字符串获取对象的字典元素?

【问题讨论】:

  • getattr(myobj, "d")['a'] 会这样做,但我想这不是你想要的。
  • 不,这不是我想要的。我希望该技术具有通用性,以便我可以使用动态构造的字符串请求对象的属性或对象的子属性/子对象/子项。我可以用“eval”来做到这一点,但我不想这样做。
  • 您将不得不创建一些复杂的函数来解析字符串,这实际上是“eval”的精简版。为什么你需要这个顺便说一句?听起来应该有更好的解决方案...

标签: python dictionary getattr


【解决方案1】:

您收到错误的原因是getattr(myobj, "d['a']") 在对象上查找名为d['a'] 的属性,但没有。您的属性名为d,它是一个字典。一旦你有了对字典的引用,然后你就可以访问其中的项目了。

mydict = getattr(myobj, "d")
val    = mydict["a"]

或者正如其他人所展示的,您可以将其合并为一个步骤(我将其显示为两个以更好地说明实际发生的情况):

val = getattr(myobj, "d")["a"]

您的问题意味着您认为对象中的字典项是该对象的“子元素”。然而,字典中的项目与对象的属性是不同的。 (不过,getattr() 也不适用于 o.a 之类的东西;它只获取一个对象的一个​​属性。如果这也是一个对象并且您想获取 its 属性之一,那就是另一个getattr()。)

您可以很容易地编写一个遍历属性路径(以字符串形式给出)并尝试将每个名称解析为字典键或属性的函数:

def resolve(obj, attrspec):
    for attr in attrspec.split("."):
        try:
            obj = obj[attr]
        except (TypeError, KeyError):
            obj = getattr(obj, attr)
    return obj

这里的基本思想是,您获取一条路径,并为路径的每个组件尝试在类似字典的容器中查找项目或对象的属性。当你到达路径的尽头时,返回你所拥有的。你的例子是resolve(myobj, "d.a")

【讨论】:

  • 感谢您的评论。您完全理解这个问题,但我希望该技术具有通用性,以便我可以使用动态构造的字符串请求对象的属性或对象的子属性/子对象/子项。我可以用“eval”来做到这一点,但我宁愿不这样做。还有其他想法吗?
【解决方案2】:

您只需使用方括号来获取字典的元素:

val = getattr(myobj, "d")["a"]

这会将val 设置为1

【讨论】:

  • 谢谢,但这不是我想要的。 (从上面复制)我希望该技术具有通用性,以便我可以使用动态构造的字符串请求对象的属性或对象的子属性/子对象/子项。我可以用“eval”来做到这一点,但我不想这样做。
【解决方案3】:

如果您还需要动态字典项,则需要在 getattr 的结果上调用 get

value = getattr(myobj, 'd').get('a')

【讨论】:

  • 我什至会更进一步,做getattr(myobj, 'd', {}).get('a')
【解决方案4】:

感谢 Kindall 的回答,我发现以下方法适用于 dict 键。

class Obj2(object):
    def __init__(self):
        self.d = {'a':'A', 'b':'B', 'c': {'three': 3, 'twothree': (2,3)}}
        self.c = 4

class MyObj(object):
    def __init__(self):
        self.d = {'a':1, 'b':2, 'c': {'two': 2, 'onetwo': (1,2)}}
        self.c = 3
        self.obj2 = Obj2()

    def resolve(self, obj, attrspec):
        attrssplit = attrspec.split(".")
        attr = attrssplit[0]
        try:
            obj = obj[attr]
        except (TypeError, KeyError):
            obj = getattr(obj, attr)
        if len(attrssplit) > 1:
            attrspec = attrspec.partition(".")[2] # right part of the string.
            return self.resolve(obj, attrspec) # Recurse
        return obj

    def __getattr__(self, name):
        return self.resolve(self, name)

# Test  
myobj = MyObj()
print getattr(myobj, "c")
print getattr(myobj, "d.a")
print getattr(myobj, "d.c.two")
print getattr(myobj, "obj2.d.a")
print getattr(myobj, "obj2.d.c.twothree")

【讨论】:

    猜你喜欢
    • 2018-12-29
    • 1970-01-01
    • 2020-08-14
    • 2015-09-02
    • 1970-01-01
    • 2017-05-26
    • 2021-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多