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.