繁体   English   中英

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

[英]Why does `getattr` not support consecutive attribute retrievals?

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

在我看来,接受后者似乎很自然。 我相信这是有充分理由的。 它是什么?

您不能在getattr函数中添加句点,因为getattr就像访问对象的字典查找(但由于子类化和其他Python实现细节,因此比之复杂一些)。

如果在a上使用“ dir”功能,则会看到与对象属性相对应的字典键。 在这种情况下,字符串“ bc” 不在字典键集中。

使用getattr做到这一点的唯一方法是嵌套调用:

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

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

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

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

测试证明;

>>> 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'>

我认为您的困惑是由于以下事实引起的:直接点表示法(ex abc )与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.bc一面是,虽然foo.bc等同于foo.__dict__['b'].__dict__['c'] ,但getattr(foo, 'b.c')等同于foo.__dict__['b.c'] 这就是为什么getattr不能按您期望的那样工作。

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

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

表示: 访问“ a”引用的对象的“ bc”属性 显然,您的对象没有名为“ bc ”的属性。

要获取“ c”属性,必须使用两个getattr调用:

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

让我们对其进行包装以更好地理解:

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

我认为实现所需目标的最直接方法是使用operator.attrgetter

>>> import operator
>>> class B():
...   c = 'foo'
... 
>>> class A():
...   b = B()
... 
>>> a = A()
>>> operator.attrgetter('b.c')(a)
'foo'

如果该属性不存在,则将出现AttributeError

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

应该返回什么getattr('a.b', {'a': None}, 'default-value'} ?它应该引发AttributeError还是只返回'default-value' ?这就是为什么如果在getattr引入了复杂的键会使它变晦涩的使用。

因此,将getattr(..)函数视为对象属性字典的get方法更为自然。

您可以通过分割点运算符并为每个点运算符执行getattr()来调用多个getattr而无需在函数内调用函数

def multi_getattr(self,obj, attr, default = None):
          attributes = attr.split(".")
          for i in attributes:
              try:
                  obj = getattr(obj, i)
              except AttributeError:
                  if default:
                      return default
                  else:
                      raise
          return obj

如果您想调用abcd,则可以通过a.multi_getattr('bcd')进行。 这将使操作通用化,而不必担心字符串中一个点运算的数量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM