Dynamically get dict elements through getattr?

I want to dynamically query objects from the class I would like to receive. getattr is similar to what I want, and it works great for top-level objects in a class. However, I would also like to indicate the subelements.

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 

How can I get the dictionary elements of an object through a string?

+4
source share
4 answers

The reason you get the error message is because getattr(myobj, "d['a']") looking for an attribute named d['a'] object, but it is not. Your attribute has a name d and a dictionary. When you have a link to a dictionary, you can access the elements in it.

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

Or, as others have shown, you can combine this in one step (I showed it as two in order to better illustrate what is actually happening):

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

Your question implies that you think the dictionary elements in the object are "sub-elements" of the object. However, the element in the dictionary is different from the attribute of the object. ( getattr() also does not work with something like oa , it just gets one attribute of one object. If this object also wants to get one of its attributes, then another getattr() .)

You can easily write a function that moves along the attribute path (given in the string) and tries to resolve each name either as a dictionary key or attribute:

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

The main idea here is that you take a path and for each component of the path, try to find either an element in the container or as an attribute of an object. When you get to the end of the path, return what you have. Your example will resolve(myobj, "da")

+7
source

You just use square brackets to get the dictionary element:

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

This will set val to 1 .

+3
source

If you need a dictionary element that will be dynamic, you need to call get as a result of getattr :

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

Thanks to the answer to Kindall, I found the following works well for key keys that are bites.

 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, "da") print getattr(myobj, "dctwo") print getattr(myobj, "obj2.da") print getattr(myobj, "obj2.dctwothree") 
+1
source

Source: https://habr.com/ru/post/1394242/


All Articles