Reputation: 3741
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
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
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