最简单方式是用 globals() 获取当前模块中已定义的类并调用构造函数;跨模块需 importlib.import_module() 加 getattr();禁用 eval/exec 防安全风险;参数传递须校验类型与必填项。

直接用 globals() 获取类对象再调用构造函数最简单
如果你的类定义在当前模块(比如 main.py 或交互式会话中),且类名是已知字符串(如 "User"),globals() 是最快捷的方式。它返回当前全局命名空间的字典,键为变量/类名,值为对应对象。
常见错误是忽略类名拼写大小写、或类尚未被导入/执行——globals() 只包含已加载到当前作用域的符号,不会自动导入模块。
- 确保类已定义:运行过
class User:定义,或已import过该类 - 类名字符串必须完全匹配:用
"User",不能用"user"或"USER" - 不适用于嵌套类或非全局类(如函数内定义的类)
- 示例:
class User: def __init__(self, name): self.name = name <p>cls_name = "User" cls = globals()[cls_name] obj = cls(name="Alice") # ✅ 正确
跨模块时必须用 getattr() + importlib.import_module()
当类在其他模块里(比如 models.py 中的 Post 类),globals() 找不到它,必须先导入模块,再用 getattr() 提取类。硬编码 import models 不行,因为模块名也是字符串变量。
容易踩的坑是路径错误、模块未安装、或相对导入在脚本中失效。推荐始终用绝对导入路径,并捕获 ImportError 和 AttributeError。
立即学习“Python免费学习笔记(深入)”;
- 模块路径用点号分隔:
"myapp.models",不是"myapp/models.py" -
getattr(module, cls_name)第二个参数必须是字符串,且严格匹配类名 - 若模块含副作用(如初始化数据库连接),动态导入会触发它,需评估副作用影响
- 示例:
import importlib <p>module_path = "myapp.models" cls_name = "Post"</p><p>module = importlib.import_module(module_path) cls = getattr(module, cls_name) obj = cls(title="Hello")
eval() 和 exec() 能用但强烈不建议
有人会想到用 eval("User()") 或拼接字符串后 exec(),这在极少数受控环境(如本地调试、配置驱动的玩具项目)可能“能跑”,但实际工程中等于主动引入远程代码执行漏洞。
只要输入字符串来自任何不可信源(配置文件、API 请求、用户表单),eval 就可能执行任意代码,包括 os.system("rm -rf /")。Python 官方文档明确将 eval 列为危险操作。
- 即使加了白名单校验,字符串拼接逻辑也极易绕过
- 静态分析工具(如
bandit)会直接报高危警告 -
eval无法处理带参数的复杂初始化(如需传入实例、依赖注入等) - 替代方案永远优先选
globals()或importlib + getattr()
带参数初始化时注意构造函数签名和类型安全
动态创建实例后,你通常要传参。但字符串无法携带类型信息,如果类构造函数要求特定类型(如 datetime、自定义枚举),而你只传字符串,就会在运行时报错,且 IDE 和类型检查器(mypy)完全无法提示。
这不是动态实例化本身的问题,而是数据绑定层缺失导致的。真实项目中,往往需要额外一层映射或 schema 来声明参数规则。
- 避免直接把 JSON 字段名硬映射为构造参数名,字段缺失或类型不符时静默失败
- 建议封装一个工厂函数,对关键参数做预检查:
if "name" not in data: raise ValueError("missing name") - 若用 Pydantic 模型,可先用
model_validate(data)做校验,再转成目标类实例 - 不要假设所有类都接受
**kwargs:有些类强制关键字参数、有些有@dataclass的__post_init__逻辑
真正难的从来不是“怎么调用”,而是“怎么保证调用时传进去的东西是它想要的”——尤其是类定义分散、参数结构多变、且错误发生在运行时的时候。


















