2025, Sep 28 23:00

Dynamic attribute access in Python: set attributes in a loop and read them with getattr or __dict__

Learn how to set and read dynamic attributes in Python loops using setattr, getattr, and __dict__. Avoid instance.tag pitfalls with name-based lookup patterns.

Dynamic attribute access in Python often looks straightforward until you try to read those attributes in a loop. Writing obj.i inside the loop won’t pull values for a, b, or c, and that’s the sticking point. The goal is to iterate, set attributes based on data, and then retrieve them in the same iterative flow.

Problem setup

Consider this minimal pattern where attributes are added at runtime and then accessed:

instance = DemoType()
labels = ['a', 'b', 'c']
for tag in labels:
  attr_id = f"{tag}"
  setattr(instance, attr_id, [f"{tag}"])
  print(instance.tag)

The intention is to see the values corresponding to instance.a, instance.b, and instance.c, each being a one-item list. But the last line tries to read instance.tag literally, which isn’t what was set.

What’s really going on

The loop correctly creates attributes whose names are derived from the loop values. However, accessing instance.tag doesn’t reference the variable tag; it looks for an attribute named exactly "tag". That’s why it fails when you expect to read the attributes you just created for "a", "b", and "c". To fetch attributes by a computed name, you need name-based lookup.

Solution

Attributes of a regular Python object are available as a dictionary via instance.__dict__. Indexing that mapping with the name you computed inside the loop gives you the value you just set. That resolves both printing and building a list for validation.

instance = DemoType()
labels = ['a', 'b', 'c']
for tag in labels:
  attr_id = f"{tag}"
  setattr(instance, attr_id, [f"{tag}"])
  print(instance.__dict__[attr_id])

If you prefer retrieving the value via a built-in call that mirrors setattr, use getattr with the same computed name:

instance = DemoType()
labels = ['a', 'b', 'c']
for tag in labels:
  attr_id = f"{tag}"
  setattr(instance, attr_id, [f"{tag}"])
  print(getattr(instance, attr_id))

To collect the values into a list for checking, iterate over the same keys and read them by name:

instance = DemoType()
labels = ['a', 'b', 'c']
for tag in labels:
  attr_id = f"{tag}"
  setattr(instance, attr_id, [f"{tag}"])
values_snapshot = [instance.__dict__[name] for name in labels]
print(values_snapshot)

Why this matters

When you create attributes dynamically, direct dot access with a static identifier won’t reflect the variable you computed in the loop. Name-based access keeps the code honest: you set by name, you read by name. In many designs, a dedicated dict-backed property bag inside the class is the cleanest way to model this pattern, and in some cases using a plain dict instead of attributes is the simpler route.

Takeaways

When attributes are created with setattr, read them using a computed name. Access through instance.__dict__[name] or getattr(instance, name) mirrors how you set them and avoids surprises. If your use case is essentially key-value storage, consider a dict-shaped approach to keep the API explicit and the data structure straightforward.

The article is based on a question from StackOverflow by Javier Olivares and an answer by Richard.