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

cam*_*mil 36 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
Run Code Online (Sandbox Code Playgroud)

假设后者,我似乎很自然.我确信这是有充分理由的.它是什么?

Tha*_*all 64

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

如果在a上使用'dir'函数,您将看到与对象属性对应的字典键.在这种情况下,字符串"bc" 不在字典键集合中.

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

getattr(getattr(a, "b"), "c")
Run Code Online (Sandbox Code Playgroud)

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

import operator
operator.attrgetter("b.c")(a)
Run Code Online (Sandbox Code Playgroud)

  • OP意识到......我认为他在问为什么.鉴于operator.attrgetter('a.b')(obj)_will_解析点分表示法,这不是一个不合理的问题 (11认同)
  • 这是一个非常好的和简单的解决令人不安的问题的方法。下面的答案并不那么简洁。这个答案并没有试图实现 Python 已经做过的事情,这让它很好。 (2认同)
  • **注意**:`getattr()` ***不只是***像直接查找字典那样简单!属性查找比这更复杂一些;例如,属性查找还允许[描述符对象绑定](https://docs.python.org/3/howto/descriptor.html),并且可以存在类层次结构的 MRO 遍历(涉及多个字典)。 (2认同)

res*_*ate 43

Python的内置reduce功能可实现您正在寻找的功能.这是一个简单的小助手功能,可以完成工作:

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
Run Code Online (Sandbox Code Playgroud)

测试证明;

>>> 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'>
Run Code Online (Sandbox Code Playgroud)

  • 再一次,我只是说问题的关键是*"为什么?"*,而不是*"我怎么能?"*.这个答案没有回答原来的问题.此外,这是一个非常冗长的解决方案,否则可以使用标准库完成:`operator.attrgetter('a.b')(obj)` (8认同)
  • 我发现Thane的答案非常有用.它使用标准库中的函数.而在Python 3中,无论如何都必须导入`reduce`. (5认同)
  • 这应该是接受的答案imho,Thane的答案是无用的. (3认同)
  • 请不要在 python 中引入 java ......天哪...... Thane 的回答绝对更可取。 (3认同)

Sil*_*Ray 7

我认为你的困惑源于直点符号(ex a.b.c)访问相同的参数getattr(),但解析逻辑是不同的.虽然它们本质上都是对象__dict__属性的关键,getattr()但不受对点可访问属性的更严格要求的约束.例如

setattr(foo, 'Big fat ugly string.  But you can hash it.', 2)
Run Code Online (Sandbox Code Playgroud)

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

foo.Big fat ugly string.  But you can hash it. = 2
Run Code Online (Sandbox Code Playgroud)

foo.'Big fat ugly string.  But you can hash it.' = 2
Run Code Online (Sandbox Code Playgroud)

是语法错误,因为现在你要求解释器将这些东西解析为原始代码,但这不起作用.

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

  • 我发现这个答案非常有参考价值。+1 (2认同)

Ros*_*nko 6

因为getattr那样不行.getattr获取具有给定名称(第二个参数)的给定对象(第一个参数)的属性.所以你的代码:

getattr(a, "b.c") # this raises an AttributeError
Run Code Online (Sandbox Code Playgroud)

表示:访问"a"引用的对象的"bc"属性.显然,您的对象没有名为" b.c"的属性.

要获得"c"属性,您必须使用两个getattr调用:

getattr(getattr(a, "b"), "c")
Run Code Online (Sandbox Code Playgroud)

让我们解开它以便更好地理解:

b = getattr(a, "b")
c = getattr(b, "c")
Run Code Online (Sandbox Code Playgroud)


Dav*_*ale 5

我认为实现您想要的最直接的方法是使用operator.attrgetter

>>> import operator
>>> class B():
...   c = 'foo'
... 
>>> class A():
...   b = B()
... 
>>> a = A()
>>> operator.attrgetter('b.c')(a)
'foo'
Run Code Online (Sandbox Code Playgroud)

如果该属性不存在,那么您将获得一个 AttributeError

>>> operator.attrgetter('b.d')(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: B instance has no attribute 'd'
Run Code Online (Sandbox Code Playgroud)