AlokThakur
AlokThakur

Reputation: 3741

How to access a object's attrbute by using string corresponding to name if object is attribute of some other object

I have 3 classes as below:-

class C(object):
    def __init__(self, v):
        self.var = v

class B(object):
    def __init__(self, c):
        self.c = c

class A(object):
    def __init__(self, b):
        self.b = b

I have created instances as 
c = C("required result")
b = B(c)
a = A(b)

>>> a.b.c.var
'required result'

Now I need to pass b.c.var as a string to some function and get the value of var similar to sample function as below -

`sample(a, 'b.c.var')` should return 'required result'`

What should be pythonic way to achieve this This is my attempt :-

for attr in ('b', 'c', 'var'):
    a = getattr(a, attr)
>>> print a
required result

Upvotes: 0

Views: 317

Answers (2)

Jon Clements
Jon Clements

Reputation: 142216

You can use operator.attrgetter which takes a dotted name notation, eg:

from operator import attrgetter
attrgetter('b.c.var')(a)
# 'required result'

Then if you don't like that syntax, use it to make your sample function, eg:

def sample(obj, attribute):
    getter = attrgetter(attribute)
    return getter(obj)

From the documentation linked above, the operator.attrgetter uses the equivalent of the following code:

def attrgetter(*items):
    if any(not isinstance(item, str) for item in items):
        raise TypeError('attribute name must be a string')
    if len(items) == 1:
        attr = items[0]
        def g(obj):
            return resolve_attr(obj, attr)
    else:
        def g(obj):
            return tuple(resolve_attr(obj, attr) for attr in items)
    return g

def resolve_attr(obj, attr):
    for name in attr.split("."):
        obj = getattr(obj, name)
    return obj

So in fact - your original code was just trying to do the equivalent of resolve_attr...

Upvotes: 5

RomanPerekhrest
RomanPerekhrest

Reputation: 92884

Here is the more accurate way, I suppose. (using try-except construction):

...

c = C("required result")
b = B(c)
a = A(b)

def sample(obj, path):
    path_attrs = path.split('.')    # splitting inner attributes path
    inner_attr = None

    for p in path_attrs:
        try:
            inner_attr = getattr(inner_attr if inner_attr else  obj, p)
        except AttributeError:
            print('No %s field' % p)

    print(inner_attr)

sample(a, 'b.c.var')  # will output 'required result'

Upvotes: 1

Related Questions