Python Reflection

Introduction

Reflection in Python is a powerful feature that allows a program to inspect and modify its own structure and behavior at runtime. It provides the ability to examine classes, methods, and objects, and to dynamically alter their properties and methods. This can be particularly useful for tasks such as debugging, testing, and building frameworks that require introspection.

Key Concepts

1. Inspecting Objects

Python provides several built-in functions for inspecting objects at runtime, such as type(), id(), dir(), getattr(), setattr(), hasattr(), and isinstance().

Example

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person("Alice", 30)

# Inspecting the type of an object
print(type(person))  # Output: <class '__main__.Person'>

# Getting the unique identifier of an object
print(id(person))  # Output: A unique identifier (e.g., 140687952335168)

# Listing the attributes and methods of an object
print(dir(person))  # Output: A list of attributes and methods

# Checking if an object has a specific attribute
print(hasattr(person, 'name'))  # Output: True

# Getting the value of an attribute
print(getattr(person, 'name'))  # Output: Alice

# Setting the value of an attribute
setattr(person, 'age', 35)
print(person.age)  # Output: 35

# Checking if an object is an instance of a specific class
print(isinstance(person, Person))  # Output: True

2. Inspect Module

The inspect module provides several useful functions for getting information about live objects, such as modules, classes, methods, functions, tracebacks, and the objects’ source code.

Example

import inspect

def greet(name):
    return f"Hello, {name}!"

# Getting the source code of a function
print(inspect.getsource(greet))

# Getting the name of the function
print(inspect.getmembers(greet))

# Getting the arguments of the function
print(inspect.signature(greet))

Output

def greet(name):
    return f"Hello, {name}!"

[
  ('__annotations__', {}),
  ('__call__', <method-wrapper '__call__' of function object at 0x7f0a2d5f2b80>),
  ('__class__', <class 'function'>),
  ...
  ('__str__', <method-wrapper '__str__' of function object at 0x7f0a2d5f2b80>),
  ('__subclasshook__', <built-in method __subclasshook__ of type object at 0x7f0a2d5460f0>)
]

(name)

3. Modifying Classes and Objects

Reflection allows you to modify classes and objects at runtime, which can be useful for dynamically adapting your program’s behavior.

Example

class Car:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model

car = Car("Toyota", "Corolla")

# Adding a new attribute dynamically
setattr(car, 'year', 2020)
print(car.year)  # Output: 2020

# Adding a new method dynamically
def get_full_description(self):
    return f"{self.brand} {self.model} ({self.year})"

setattr(Car, 'get_full_description', get_full_description)
print(car.get_full_description())  # Output: Toyota Corolla (2020)

4. Creating Classes Dynamically

You can use the type() function to create new classes dynamically at runtime.

Example

# Creating a new class dynamically
DynamicPerson = type('DynamicPerson', (object,), {'greet': lambda self: "Hello!"})

# Creating an instance of the dynamically created class
dp = DynamicPerson()
print(dp.greet())  # Output: Hello!

5. Examining Inheritance

Reflection can also be used to examine the inheritance hierarchy of classes.

Example

class A:
    pass

class B(A):
    pass

class C(B):
    pass

# Checking the base classes
print(C.__bases__)  # Output: (<class '__main__.B'>,)

# Getting the method resolution order
print(C.__mro__)  # Output: (<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

6. Introspection of Methods

You can inspect the methods of classes to get detailed information about their parameters, default values, and more.

Example

class MathOperations:
    def add(self, x, y):
        return x + y

method = MathOperations.add
print(inspect.signature(method))  # Output: (self, x, y)

Conclusion

Reflection in Python provides powerful capabilities to inspect and modify the structure and behavior of objects, classes, and methods at runtime. This can be incredibly useful for dynamic programming tasks such as debugging, testing, and building frameworks that require introspection. Understanding and utilizing reflection can greatly enhance your ability to write flexible and adaptable Python programs.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top