Cole
Cole

Reputation: 2509

Resolving a Python object in global namespace by string look up

Let's say you want the object at the end of strings of object names: an example string would be 'first_class.second_class.third_class.id', and the list of strings all take the form 'X1object.X2object...XNobject.what_you_want_is_here_object'.

In every case, you know that there is an active instance of the X1object, whatever its specific name. With the example string, the code has already called an instance of first_class. You can load first_class with globals['first_class'], and generically load X1object with globals['X1object'].

What you want is the object (usually a value) at then end of the string. So with the example string, you want the value of id = first_class.second_class.third_class.id. Is there an easy way to convert the string so that it fetches its end object?

Below is the code I created to handle this problem, but it seems like a brute force approach that fetches each attribute in turn until it finds the last one.

first_class = FirstClass()
first_class = go_do_something_wild_in_first_class(first_class)
...

attribute = 'first_class.second_class.third_class.id'
attribute_pieces = attribute.split('.')

fetch_attribute = lambda attribute, name: \
    attribute[name] if attribute == globals() else \
    getattr(attribute, name)

for name in attribute_pieces: # I changed the code from using an index to using a name
    if name == attribute_pieces[0]:
        attribute = fetch_attribute(globals(), name)
    else:
        attribute = fetch_attribute(attribute, name)

id = attribute

Upvotes: 3

Views: 1231

Answers (3)

Martijn Pieters
Martijn Pieters

Reputation: 1121486

You can use reduce():

def resolve_object(name):
    names = name.split('.')
    return reduce(getattr, names[1:], globals()[names[0]])

Here we simply look up names[0] as a global, then loop over the rest of the names to do a getattr for each one on the result so far.

Demo:

>>> class Foo(object): pass
... 
>>> first_class = Foo()
>>> first_class.second_class = Foo()
>>> first_class.second_class.third_class = Foo
>>> first_class.second_class.third_class.id = 'baz'
>>> resolve_object('first_class.second_class.third_class.id')
'baz'

Upvotes: 4

Fábio Diniz
Fábio Diniz

Reputation: 10353

You should use importlib

attribute = 'first_class.second_class.third_class.id'
attribute_pieces = attribute.split('.')

id = getattr(importlib.import_module('.'.join(attribute_pieces[:-1]), attribute_pieces[-1])

Upvotes: 1

Mikko Ohtamaa
Mikko Ohtamaa

Reputation: 83788

There is a Python library called zope.dottedname which does exactly what you want:

https://pypi.python.org/pypi/zope.dottedname

It resolves arbitrary strings to corresponding objects in Python namespace, including attributes of objects.

Upvotes: 1

Related Questions