目录
- python动态实例化
- 前言
- 方式1, 字典映射
- 方式2, __init_subclass__
- 补充
Python 动态创建一个类的示例代码:
# 动态创建类 # class_body 里是类里边的函数,可以写在文件里,从文件中读取 class_body = """ def running_function(self): print(self.name) print("running function") def set_name(self,name): self.name = name """ class_dict = {} # 加载类方法到class_dict里 exec(class_body,globals(),class_dict) # 把class_dict中的方法加载到类里边 Customer = type("Customer",(object,),class_dict) # 实例化类,调用类方法 c = Customer() c.set_name("12") c.running_function()
补充:python动态实例化
python动态实例化
前言
最近在查一个服务的问题时,看到有一段代码if .. elif ...
写了近百行,类似
if command == "xxx": obj = CommandX() obj.run() # ... elif command == "yyy": obj = CommandY() obj.run() # ... elif command == "zzz": obj = Commandz() obj.run() # ... # ...
翻了下git记录,最开始其实只有两三个条件判断,后来command越加越多,就这么延续下来了。
代码逻辑其实没什么问题,也很简单明了,就是看起来有点丑,而且我还开了比较高的桌面缩放,导致一屏幕几乎都是这段if ... elif
看来看去越发觉得丑,先写个demo看看能不能跑通代码。
方式1, 字典映射
如果需要判断的条件比较少,用字典做映射还是挺方便的,但如果条件多,看起来还是挺丑的。
from abc import ABC, abstractmethod class AbstractCommand(ABC): @abstractmethod def run(self): pass class CommandA(AbstractCommand): def run(self): return "this is command A" class CommandB(AbstractCommand): def run(self): return "this is command B" class CommandC(AbstractCommand): def run(self): return "this is command C" class CommandFactory: @staticmethod def create_command(command_type: str) -> AbstractCommand: command_mapping = { "cmda": CommandA, "cmdb": CommandB, "cmdc": CommandC } cls = command_mapping.get(command_type.lower()) if not cls: raise ValueError(f"Unknown command type: {command_type}") return cls() if __name__ == "__main__": cmd = CommandFactory.create_command("cmda") assert cmd.run() == "this is command A" cmd = CommandFactory.create_command("cmdb") assert cmd.run() == "this is command B" cmd = CommandFactory.create_command("cmdc") assert cmd.run() == "this is command CD" # should be exception cmd = CommandFactory.create_command("cmdd") # should be exception assert cmd.run() == "this is command D"
方式2, __init_subclass__
《流畅的Python(第2版)》的最后一章提到了这个__init__subclass__
,根据python官方文档:
当所在类派生子类时此方法就会被调用。cls 将指向新的子类。如果定义为一个普通实例方法,此方法将被隐式地转换为类方法。传给一个新类的关键字参数会被传给上级类的
__init_subcandroidlass__
。 为了与其他使用__init_subclass__
的类兼容,应当去掉需要的关键字参数再将其他参数传给基类。
借助这个机制,可以在实现抽象基类时自动注册子类,避免手IVNDncy动维护注册表。
from abc import ABCMeta, abstractmethod from threading import Lock from collections import UserDict class ThreadSafeDict(UserDict): """线程安全的字典""" def __init__(self): super().__init__() self._lock = Lock() def __setitem__(self, key, item): with self._lock: super().__setitem__(key, item) class Command(metaclass=ABCMeta): registry = ThreadSafeDict() def __init__(self): pass @abstractmethod def run(self): pass def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.registry[cls.__name__.lower()] = cls # 自动注册子类 # 子类定义即自动注册 class CommandA(编程客栈Command): def run(self): return "this is command a!" class CommandB(Command)php: def run(self): return "this is command b!" class CommandC(Command): def run(self): return "this is command b!" def create_command(command_type: str) -> Command: """工厂函数""" cls = Command.registry.get(command_type.lower()) if not cls: raise ValueError(f"Unknown command type: {command_type}") return cls() if __name__ == "__main__": cmd = create_command("CommandA") assert cmd.run() == "this is command a!" cmd = create_command("CommandB") assert cmd.run() == "this is command b!" cmd = create_command("CommandC") assert cmd.run() == "this is command cc!" # should be exception cmd = create_command("CommandD") assert cmd.run() == "this is command b!" # should be exceptiwww.devze.comon
乍一看还是挺不错的,但是也有个缺点,那就是如果各个类分散在不同模块中,那么工厂函数所在的模块就要写一堆from xxx import ...
如果module和类命名比较规范,也可以这么动态加载类
import importlib def create_class(module_name, class_name): module = importlib.import_module(module_name) cls = getattr(module, class_name) return cls()
补充
自动注册类看起来炫,但是对代码阅读来说不是很直观。易读还是美观?这是一个问题。
到此这篇关于Python 动态创建一个类的文章就介绍到这了,更多相关Python 动态创建一个类内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论