Keywords: Python method retrieval | inspect module | class reflection | metaprogramming | dir function
Abstract: This article provides an in-depth exploration of various techniques for obtaining method lists in Python classes, with a focus on the inspect module's getmembers function and its predicate parameter. It compares different approaches including the dir() function, vars() function, and __dict__ attribute, analyzing their respective use cases. Through detailed code examples and performance analysis, developers can choose the most appropriate method based on specific requirements, with compatibility solutions for Python 2.x and 3.x versions. The article also covers method filtering, performance optimization, and practical application scenarios, offering comprehensive guidance for Python metaprogramming and reflection techniques.
Overview of Python Class Method Retrieval Techniques
In Python programming, obtaining a list of all methods in a class is a common requirement, particularly in dynamic programming, debugging, and metaprogramming scenarios. This article systematically introduces multiple techniques for retrieving class methods and provides in-depth analysis of their respective advantages, disadvantages, and applicable situations.
Using the inspect Module for Method Retrieval
The inspect module in Python's standard library offers the most professional approach for method retrieval. The inspect.getmembers() function, combined with the predicate parameter, enables precise filtering of class methods.
Basic Usage Example
import inspect
from optparse import OptionParser
# Retrieve class method list
class_methods = inspect.getmembers(OptionParser, predicate=inspect.ismethod)
print("Class methods:", class_methods)
# Retrieve instance method list
parser = OptionParser()
instance_methods = inspect.getmembers(parser, predicate=inspect.ismethod)
print("Instance methods:", instance_methods)
Python Version Compatibility Handling
Method type determination differs between Python 2.x and 3.x, requiring appropriate predicates for different versions:
import inspect
import sys
class ExampleClass:
def instance_method(self):
pass
@classmethod
def class_method(cls):
pass
@staticmethod
def static_method():
pass
# Python version adaptation
if sys.version_info[0] < 3:
# Python 2.x
methods = inspect.getmembers(ExampleClass, predicate=inspect.ismethod)
else:
# Python 3.x
methods = inspect.getmembers(ExampleClass, predicate=inspect.isfunction)
print("Method list:", methods)
Using dir() Function with callable Filtering
The dir() function provides an alternative approach for accessing class members, but requires manual filtering for callable methods:
class MyClass:
def method1(self):
return "Method 1"
def method2(self):
return "Method 2"
attribute = "Non-method attribute"
# Retrieve all callable methods
method_list = [func for func in dir(MyClass) if callable(getattr(MyClass, func))]
print("All callable methods:", method_list)
# Exclude dunder methods
clean_methods = [func for func in dir(MyClass)
if callable(getattr(MyClass, func)) and not func.startswith("__")]
print("Cleaned method list:", clean_methods)
Using vars() Function and __dict__ Attribute
Access method information through direct namespace access:
class DataProcessor:
def process_data(self, data):
return data.upper()
def validate_input(self, input_data):
return isinstance(input_data, str)
config = {"encoding": "utf-8"}
# Using vars() function
methods_vars = [method for method in vars(DataProcessor)
if callable(getattr(DataProcessor, method)) and not method.startswith("__")]
print("Methods using vars():", methods_vars)
# Using __dict__ attribute
methods_dict = [method for method in DataProcessor.__dict__
if callable(getattr(DataProcessor, method)) and not method.startswith("__")]
print("Methods using __dict__:", methods_dict)
Advanced Filtering and Classification Techniques
In practical applications, more refined classification and filtering of methods is often required:
import inspect
class AdvancedClass:
def public_method(self):
"""Public method"""
pass
def _protected_method(self):
"""Protected method"""
pass
def __private_method(self):
"""Private method"""
pass
@classmethod
def class_method(cls):
pass
@staticmethod
def static_method():
pass
# Retrieve all public methods (not starting with _)
public_methods = [name for name, method in inspect.getmembers(AdvancedClass, predicate=inspect.ismethod)
if not name.startswith('_')]
print("Public methods:", public_methods)
# Retrieve class methods and static methods
class_methods = [name for name, method in inspect.getmembers(AdvancedClass)
if isinstance(method, classmethod)]
static_methods = [name for name, method in inspect.getmembers(AdvancedClass)
if isinstance(method, staticmethod)]
print("Class methods:", class_methods)
print("Static methods:", static_methods)
Performance Comparison and Best Practices
Different methods exhibit varying performance characteristics, requiring selection based on specific scenarios:
import timeit
import inspect
class PerformanceTest:
def method1(self): pass
def method2(self): pass
def method3(self): pass
# Performance testing
def test_inspect():
return inspect.getmembers(PerformanceTest, predicate=inspect.ismethod)
def test_dir():
return [func for func in dir(PerformanceTest) if callable(getattr(PerformanceTest, func))]
# Execution time comparison
inspect_time = timeit.timeit(test_inspect, number=1000)
dir_time = timeit.timeit(test_dir, number=1000)
print(f"inspect module execution time: {inspect_time:.6f} seconds")
print(f"dir() function execution time: {dir_time:.6f} seconds")
Practical Application Scenarios
These techniques are particularly useful in the following scenarios:
class PluginSystem:
"""Plugin system example"""
def __init__(self):
self.plugins = []
def register_plugin(self, plugin_class):
"""Automatically register all methods starting with 'plugin_' as plugins"""
plugin_methods = [method for method in dir(plugin_class)
if callable(getattr(plugin_class, method))
and method.startswith('plugin_')]
for method_name in plugin_methods:
self.plugins.append(getattr(plugin_class, method_name))
print(f"Registered plugin methods: {plugin_methods}")
class TextProcessor:
def plugin_uppercase(self, text):
return text.upper()
def plugin_lowercase(self, text):
return text.lower()
def internal_method(self):
pass
# Using the plugin system
system = PluginSystem()
system.register_plugin(TextProcessor)
Error Handling and Edge Cases
Various edge cases need consideration in practical usage:
import inspect
class EdgeCaseClass:
def normal_method(self):
pass
def __dunder_method__(self):
pass
@property
def computed_property(self):
return "Computed property"
# Safely retrieve methods
def safe_get_methods(cls):
try:
methods = inspect.getmembers(cls, predicate=inspect.ismethod)
# Filter out special methods and properties
filtered_methods = [name for name, method in methods
if not name.startswith('__') and not isinstance(method, property)]
return filtered_methods
except Exception as e:
print(f"Error retrieving methods: {e}")
return []
result = safe_get_methods(EdgeCaseClass)
print("Safely retrieved method list:", result)
Through the various techniques introduced in this article, developers can select the most appropriate method retrieval strategy based on specific requirements. The inspect module provides the most professional and precise solution, while the dir() function and namespace access offer lighter-weight alternatives. In actual projects, it is recommended to choose the appropriate solution based on performance requirements, Python version compatibility, and code readability.