object-oriented programming
python classes
class attributes
coding tutorial
software development

Getting attributes of a class

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Python, getting attributes from a class can mean different things: class attributes, instance attributes, inherited attributes, descriptors, or methods. The best introspection approach depends on what you want to include and how dynamic your objects are. Clear attribute discovery rules make debugging, serialization, and framework integration much more reliable.

Class Attributes vs Instance Attributes

Class attributes live on the class object and are shared. Instance attributes live on each object instance.

python
1class User:
2    role = "member"  # class attribute
3
4    def __init__(self, name, age):
5        self.name = name  # instance attribute
6        self.age = age
7
8u = User("Rae", 29)
9print(User.role)
10print(u.name, u.age)

If you introspect without distinguishing these two categories, output can be confusing.

Get Instance Attributes Safely

For regular objects, vars(instance) or instance.__dict__ returns instance data attributes.

python
print(vars(u))
print(u.__dict__)

This is often the right choice for JSON serialization of object state, but it excludes properties computed dynamically and may not work for objects using slots.

Inspect Class Level Members

For class level inspection, use vars(ClassName) or ClassName.__dict__.

python
class_dict = vars(User)
print(class_dict.keys())

This includes methods, descriptors, and metadata, not only business fields. Filter what you need rather than assuming every key is a plain attribute.

Discover Attributes Across Inheritance

dir(obj) returns names from object, class, and inherited classes. It is broad and useful for exploration.

python
print(dir(u)[:20])

Use getattr to read values dynamically:

python
for name in ["name", "role", "missing"]:
    print(name, getattr(u, name, None))

Providing a default value avoids exceptions on missing names.

Filter Data Attributes from Callables

If you need only non method attributes:

python
1def data_attributes(obj):
2    out = {}
3    for name in dir(obj):
4        if name.startswith("_"):
5            continue
6        value = getattr(obj, name)
7        if callable(value):
8            continue
9        out[name] = value
10    return out
11
12print(data_attributes(u))

This includes inherited data and properties while skipping methods.

Be careful with properties, though. Accessing them through getattr can run code, hit the database, or raise errors. For diagnostic tools, you may prefer inspecting descriptors on the class rather than eagerly evaluating every attribute on the instance.

Dataclasses and Typed Models

For dataclasses, use fields for explicit schema based introspection.

python
1from dataclasses import dataclass, fields
2
3@dataclass
4class Product:
5    sku: str
6    price: float
7
8print([f.name for f in fields(Product)])

This is cleaner than generic reflection when working with structured model objects.

Objects with __slots__

Classes using __slots__ may not have __dict__. In those cases, inspect slot names directly.

python
1class Point:
2    __slots__ = ("x", "y")
3
4    def __init__(self, x, y):
5        self.x = x
6        self.y = y
7
8p = Point(2, 3)
9print(p.x, p.y)

If your utility assumes __dict__, it will fail on slot based objects.

Annotations and inspect for Richer Introspection

When class metadata matters, inspect type annotations and descriptors separately from runtime values.

python
1import inspect
2
3class Order:
4    id: int
5    total: float
6
7    def __init__(self, id, total):
8        self.id = id
9        self.total = total
10
11print(Order.__annotations__)
12print([name for name, _ in inspect.getmembers(Order, predicate=inspect.isfunction)])

This separation helps tools that generate documentation, validators, or schema mappings.

Practical Serialization Strategy

When building generic serializers, pick one explicit policy:

  • only instance __dict__ fields.
  • include properties.
  • include inherited attributes.

Avoid ambiguous reflection helpers that change behavior between classes. Stable policies reduce subtle bugs in API responses and caching logic.

If the object comes from a library, check whether it already exposes a serialization helper before building your own reflection layer. Library specific serializers are often safer than generic attribute walking.

Common Pitfalls

  • Mixing class and instance attributes without clear output labeling.
  • Assuming __dict__ exists on all objects.
  • Using dir output directly for serialization without filtering callables.
  • Ignoring inherited attributes when they are semantically important.
  • Dynamically reading missing attributes without safe defaults.

Summary

  • Decide what kind of attributes you need before choosing introspection tools.
  • Use vars or __dict__ for instance state when available.
  • Use class dictionaries for class level inspection.
  • Use dir and getattr for broader reflective discovery.
  • Handle dataclasses and slot based objects explicitly for robust utilities.

Course illustration
Course illustration

All Rights Reserved.