You could try:
import re
extended_split = re.compile(r'''\[\d+\]|[^\[.]+''').findall
def extended_getattr(obj, comp):
if comp[0] == '[':
return obj[int(comp[1:-1])]
else:
return getattr(obj, comp)
reduce(extended_getattr, extended_split('xattr.yattr[2].zattr'), myobject)
Note that it assumes the stuff inside […]
is a nonnegative decimal number.
In case you concern about performance, it is still faster than eval
in my test:
~:491$ python -m timeit -s 'from z import f1, f3, f, rs' 'f3(rs, "f")' # eval
100 loops, best of 3: 5.62 msec per loop
~:492$ python -m timeit -s 'from z import f1, f3, f, rs' 'f1(rs, f)' # my method
100 loops, best of 3: 4.69 msec per loop
Content of z.py
:
import re
import random
from functools import reduce
extended_split = re.compile(r'''\[\d+\]|[^\[.]+''').findall
def extended_getattr(obj, comp):
if comp[0] == '[':
return obj[int(comp[1:-1])]
else:
return getattr(obj, comp)
class Foo(object):
def __init__(self):
self.foo = self
def __getitem__(self, i):
return self
def construct_random_string():
yield 'foo'
for i in range(2000):
if random.randrange(2):
yield '.foo'
else:
yield '[0]'
random.seed(0) # to ensure fair comparison
rs = ''.join(construct_random_string())
f = Foo()
def f1(names, obj):
return reduce(extended_getattr, extended_split(names), obj)
def f3(attrstring, objname) :
return eval( '%s.%s' % (objname, attrstring) )