为什么“getattr”不支持连续的属性检索?

2024-04-27 02:58:25 发布

您现在位置:Python中文网/ 问答频道 /正文

class A(): pass

a = A()
b = A()

a.b = b
b.c = 1

a.b     # this is b
getattr(a, "b") # so is this

a.b.c   # this is 1   
getattr(a, "b.c") # this raises an AttributeError

对我来说,假设是后者似乎很自然。我相信这是有原因的。它是什么?


Tags: ansois原因passthisclassattributeerror
3条回答

我认为您的困惑源于这样一个事实:直点表示法(exa.b.c)访问的参数与getattr()相同,但解析逻辑不同。虽然它们本质上都是对象的__dict__属性的键,但是getattr()并没有绑定到对点可访问属性更严格的要求。例如

setattr(foo, 'Big fat ugly string.  But you can hash it.', 2)

是有效的,因为该字符串只是成为foo.__dict__中的哈希键,但是

foo.Big fat ugly string.  But you can hash it. = 2

以及

foo.'Big fat ugly string.  But you can hash it.' = 2

是语法错误,因为现在您要求解释器将这些内容解析为原始代码,但这不起作用。

另一方面,当foo.b.c等同于foo.__dict__['b'].__dict__['c']时,getattr(foo, 'b.c')等同于foo.__dict__['b.c']。这就是为什么getattr不能像您期望的那样工作。

Python's built-in ^{} function启用您要查找的功能。下面是一个简单的小助手函数,它可以完成任务:

class NoDefaultProvided(object):
    pass

def getattrd(obj, name, default=NoDefaultProvided):
    """
    Same as getattr(), but allows dot notation lookup
    Discussed in:
    http://stackoverflow.com/questions/11975781
    """

    try:
        return reduce(getattr, name.split("."), obj)
    except AttributeError, e:
        if default != NoDefaultProvided:
            return default
        raise

试验证明

>>> getattrd(int, 'a')
AttributeError: type object 'int' has no attribute 'a'

>>> getattr(int, 'a')
AttributeError: type object 'int' has no attribute 'a'

>>> getattrd(int, 'a', None)
None

>>> getattr(int, 'a', None)
None

>>> getattrd(int, 'a', None)
None

>>> getattrd(int, '__class__.__name__')
type

>>> getattrd(int, '__class__')
<type 'type'>

不能在getattr函数中放置句点,因为getattr类似于访问对象的字典查找(但由于子类化和其他Python实现细节,比这个复杂一点)。

如果在上使用“dir”函数,将看到与对象属性对应的字典键。在本例中,字符串“b.c”不在字典键集中。

getattr执行此操作的唯一方法是嵌套调用:

getattr(getattr(a, "b"), "c")

幸运的是,标准库有更好的解决方案!

import operator
operator.attrgetter("b.c")(a)

相关问题 更多 >