24.反射和动态属性
反射和动态属性
概念
反射是指通过一组内置的函数和语句,在运行时修改对象的能力。
允许动态创建类、调用方法、获取和设置属性,以及修改对象的行为。
class MyClass:
pass
DyClass = type("DyClass", (), {}) # 动态创建类
my_instance = DyClass() # 创建类的实例
主要场景
- 动态加载模块和类:使用反射可以在运行时动态加载模块和类,以便于程序更加灵活和可扩展。
- 动态修改对象属性和方法:使用反射可以在运行时动态修改对象的属性和方法,以便于程序更加灵活。
- 实现插件系统:使用反射可以实现插件系统,允许程序在运行时动态加载和卸载插件。
- 实现ORM框架:使用反射可以实现ORM框架,允许程序在运行时动态地将Python对象映射到数据库中的表格。
属性和方法
hasattr(object,'attrName')
:判断该对象是否有指定名字的属性或方法,返回值是bool类型getattr(object,'attrName')
:获取对象指定名称的属性或方法,返回值是str类型setattr(object,'attrName',value)
:给指定的对象添加属性以及属性值,可以通过对象、字符串和值的方式传递delattr(object,'attrName')
:删除对象指定名称的属性或方法值,无返回值dir()
:获取对象的所有属性和方法的列表。type()
:获取对象的类型。
动态创建导入
# 动态创建对象
class MyClass:
def __init__(self, x, y):
self.x = x
self.y = y
my_class = type("MyClass", (), {"x": 1, "y": 2})
my_object = my_class()
print(my_object.x, my_object.y) # 输出 1 2
# 动态调用模块中的函数
# 使用 importlib.import_module() 导入模块
import importlib
module_name = 'math'
module = importlib.import_module(module_name)
# 使用 getattr() 访问模块中的函数
sqrt_function = getattr(module, 'sqrt')
result = sqrt_function(4)
print(result) # 输出: 2.0
调用和访问
# 访问对象属性
class MyClass:
def __init__(self, x):
self.x = x
obj = MyClass(42)
attr_name = "x"
attr_value = getattr(obj, attr_name)
print(f"{attr_name} = {attr_value}")
# 动态调用对象方法
class MyClass:
def my_method(self, x, y):
return x + y
my_object = MyClass()
result = getattr(my_object, "my_method")(1, 2)
print(result) # 输出 3
# 动态导入模块
# 使用 importlib.import_module() 导入模块
import importlib
module_name = 'math'
module = importlib.import_module(module_name)
# 使用 getattr() 访问模块的属性
pi_value = getattr(module, 'pi')
print(pi_value) # 输出: 3.141592653589793
# 获取类属性
class MyClass:
my_class_attribute = "Hello World"
print(getattr(MyClass, "my_class_attribute")) # 输出 "Hello World"
检查对象属性
# 检查对象是否具有属性
class MyClass:
def __init__(self):
self.my_attribute = "Hello World"
my_object = MyClass()
print(hasattr(my_object, "my_attribute")) # 输出 True
print(hasattr(my_object, "non_existent_attribute")) # 输出 False
# 动态获取类的方法列表
class MyClass:
def __init__(self):
self.my_attribute = 'Hello, World!'
def my_method(self):
print(self.my_attribute)
# 使用 dir() 获取类的方法列表
method_list = [method_name for method_name in dir(MyClass) if callable(getattr(MyClass, method_name))]
print(method_list) # 输出: ['__init__', '__module__', 'my_method']
动态修改属性
# 动态修改对象的属性
class MyClass:
def __init__(self):
self.my_attribute = 'Hello, World!'
my_object = MyClass()
# 使用 setattr() 修改对象的属性
setattr(my_object, 'my_attribute', 'Hello, Universe!')
print(my_object.my_attribute) # 输出: 'Hello, Universe!'
inspect模块
inspect 模块提供了一些有用的函数帮助获取对象的信息,例如模块、类、方法、函数、回溯、帧对象以及代码对象。
inspect 模块主要提供了四种功能
- (1) 对是否是模块,框架,函数等进行类型检查。
- (2) 获取源码
- (3) 获取类或函数的参数的信息
- (4) 解析堆栈
inspect.getmembers 函数用于返回一个对象上的所有成员,其返回值是一个键值对为元素的列表。
函数 | 定义 | 功能 |
---|---|---|
inspect.ismodule | inspect.ismodule(object) | 当该对象是一个模块时返回 True。 |
inspect.isclass | inspect.isclass(object) | 当该对象是一个类时返回 True,无论是内置类或者 Python 代码中定义的类。 |
inspect.ismethod | inspect.ismethod(object) | 当该对象是一个 Python 写成的绑定方法时返回 True。 |
inspect.isfunction | inspect.isfunction(object) | 当该对象是一个 Python 函数时(包括使用 lambda 表达式创造的函数),返回 True。 |
import inspect
class TestClass():
def __init__(self):
self.data = {}
self.path = ""
def test(self):
pass
# print(inspect.getmembers(TestClass)) # 获取所有成员,包含了属性和函数
# 可选参数 predicate 可以筛选指定的成员
print(inspect.getmembers(
TestClass,
predicate = lambda obj: inspect.isfunction(obj) and obj.__name__.startswith('test')
))
[('test', <function TestClass.test at 0x000002A051F62790>)]
官方文档 https://docs.python.org/zh-cn/3/library/inspect.html
实战-插件调用
# 插件接口定义
class PluginInterface:
def run(self):
pass
# 插件实现类1
class Plugin1(PluginInterface):
def run(self):
print("Running Plugin 1")
# 插件实现类2
class Plugin2(PluginInterface):
def run(self):
print("Running Plugin 2")
# 插件管理器
class PluginManager:
def __init__(self):
self.plugins = {}
def load_plugins(self, plugin_module_names):
for module_name in plugin_module_names:
try:
# 动态加载模块
module = __import__(module_name)
# 遍历模块的成员
for member_name in dir(module):
member = getattr(module, member_name)
# 检查成员是否为插件实现类
if (
isinstance(member, type)
and issubclass(member, PluginInterface)
and member != PluginInterface
):
# 创建插件实例
plugin = member()
# 注册插件
self.plugins[module_name] = plugin
except ImportError:
print(f"无法加载模块:{module_name}")
def run_plugins(self):
for module_name, plugin in self.plugins.items():
print(f"Running plugin from module: {module_name}")
plugin.run()
# 主程序
if __name__ == "__main__":
# 创建插件管理器
manager = PluginManager()
# 加载插件模块
manager.load_plugins(["plugin1", "plugin2"])
# 运行插件
manager.run_plugins()
动态代码生成
import inspect
class Extracter(object):
def extract(self, data):
return {
extract_method_name.split("_", 1)[-1]: extract_method(data)
for extract_method_name, extract_method in inspect.getmembers(self, inspect.ismethod)
if extract_method_name.startswith("extract_")
}
def extract_title(self, data):
return f"{data}-title"
def extract_context(self, data):
return f"{data}-context"
def extract_pubdate(self, data):
return f"{data}-pubdate"
data = Extracter().extract("text")
print(data)
# {'title': 'text-title', 'context': 'text-context', 'pubdate': 'text-pubdate'}
简单工程模式
from abc import ABCMeta, abstractmethod
# 产品基类
class Product(metaclass=ABCMeta):
@abstractmethod
def use(self):
pass
# 产品A
class ConcreteProductA(Product):
"""A"""
def __init__(self) -> None:
super().__init__()
pass
def use(self):
print(f'[{self.__class__.__name__}]: use')
@classmethod
def doc(cls):
return 'A'
# 产品B
class ConcreteProductB(Product):
"""B"""
def __init__(self) -> None:
super().__init__()
pass
def use(self):
print(f'[{self.__class__.__name__}]: use')
@classmethod
def doc(cls):
return 'B'
print([_cls.doc() for _cls in Product.__subclasses__()])
class Factory(object):
@staticmethod
def create(product_name):
# 构建映射表
products = {_cls.doc(): _cls for _cls in Product.__subclasses__() if _cls.doc and _cls.__name__ != "Product"}
print(products)
return products[product_name]()
A=Factory.create("A")
A.use()
# [ConcreteProductA]: use
B=Factory.create("B")
B.use()
# [ConcreteProductB]: use
参考资料
https://mp.weixin.qq.com/s/NRSkzdAbcdF828YhFd88NA
https://juejin.cn/post/7301579823414607898
https://juejin.cn/post/7280439887966863360?searchId=2023120410572214AADC9B1A87571DF03D#heading-5 (工厂模式)