Why does `getattr` not support consecutive attribute retrievals?

后端 未结 7 1234
梦如初夏
梦如初夏 2021-02-01 01:53
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 Attrib         


        
相关标签:
7条回答
  • 2021-02-01 02:17

    Python's built-in reduce function enables the functionality you're looking for. Here's a simple little helper function that will get the job done:

    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
    

    Test proof;

    >>> 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'>
    
    0 讨论(0)
  • 2021-02-01 02:18

    I think the most straight forward way to achieve what you want is to use operator.attrgetter.

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

    If the attribute doesn't exist then you'll get an AttributeError

    >>> operator.attrgetter('b.d')(a)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: B instance has no attribute 'd'
    
    0 讨论(0)
  • 2021-02-01 02:28

    What should return getattr('a.b', {'a': None}, 'default-value'}? Should it raise AttributeError or just return 'default-value'? That's why complex keys if introduced in getattr would make it obscure to use.

    So, it's more natural to view getattr(..) function as get method of dictionary of object attributes.

    0 讨论(0)
  • 2021-02-01 02:29

    I think your confusion arises from the fact that straight dot notation (ex a.b.c) accesses the same parameters as getattr(), but the parsing logic is different. While they both essentially key in to an object's __dict__ attribute, getattr() is not bound to the more stringent requirements on dot-accessible attributes. For instance

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

    Is valid, since that string just becomes a hash key in foo.__dict__, but

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

    and

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

    are syntax errors because now you are asking the interpreter to parse these things as raw code, and that doesn't work.

    The flip side of this is that while foo.b.c is equivalent to foo.__dict__['b'].__dict__['c'], getattr(foo, 'b.c') is equivalent to foo.__dict__['b.c']. That's why getattr doesn't work as you are expecting.

    0 讨论(0)
  • 2021-02-01 02:32

    You can call the multiple getattr without calling a function within function by splitting the dot operators and performing a getattr() for each dot operator

    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
    

    If suppose you wish to call a.b.c.d you can do it via a.multi_getattr('b.c.d'). This will generalise the operation without worrying about the count of dot operation one has in the string.

    0 讨论(0)
  • 2021-02-01 02:39

    You can't put a period in the getattr function because getattr is like accessing the dictionary lookup of the object (but is a little bit more complex than that, due to subclassing and other Python implementation details).

    If you use the 'dir' function on a, you'll see the dictionary keys that correspond to your object's attributes. In this case, the string "b.c" isn't in the set of dictionary keys.

    The only way to do this with getattr is to nest calls:

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

    Luckily, the standard library has a better solution!

    import operator
    operator.attrgetter("b.c")(a)
    
    0 讨论(0)
提交回复
热议问题