[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_subclass__。 为了与其他使用__init_subclass__的类兼容,应当去掉需要的关键字参数再将其他参数传给基类。
借助这个机制,可以在实现抽象基类时自动注册子类,避免手动维护注册表。
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):
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 exception
乍一看还是挺不错的,但是也有个缺点,那就是如果各个类分散在不同模块中,那么工厂函数所在的模块就要写一堆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 动态创建函数【转】
知乎上也有相似的问题 偶然碰到一个问题,初想是通过动态创建Python函数的方式来解决,于是调研了动态创建Python函数的方法. 定义lambda函数 在Python中定义lambda函数的写法很简 ...
- PHP动态实例化对象并向构造函数传递参数
在框架开发,模块化开发等场合,我们可能有一种需求,那就是在PHP运行时动态实例化对象. 什么是动态实例化对象呢?我们先来看一下PHP有一种变量函数(可变函数)的概念,例如如下代码: function ...
- python动态类型
在python中,省去了变量声明的过程,在引用变量时,往往一个简单的赋值语句就同时完成了,声明变量类型,变量定义和关联的过程,那么python的变量到底是怎样完成定义的呢? 动态类型 python使用 ...
- c# 动态实例化一个泛型类
动态实例化一个类,比较常见,代码如下 namespace ConsoleApp2 { public class MyClass { } } Type classType = Type.GetType( ...
- 【Python】python动态类型
在python中,省去了变量声明的过程,在引用变量时,往往一个简单的赋值语句就同时完成了,声明变量类型,变量定义和关联的过程,那么python的变量到底是怎样完成定义的呢? 动态类型 python使用 ...
- python爬虫的一些小小问题、python动态正则表达式
1.首先urllib不能用了,需要引入的是urllib2,正则re. #coding=utf-8 # import urllib import urllib2 import re def getHtm ...
- python动态添加属性和方法
---恢复内容开始--- python动态添加属性: class Person(object): def __init__(self,newName,newAge): self.name = newN ...
- ABP框架 - 介绍 VS2017调试器无法附加到IIS进程(w3wp.exe) c# 动态实例化一个泛型类
ABP框架 - 介绍 在14,15年间带领几个不同的团队,交付了几个项目,在这个过程中,虽然几个项目的业务不一样,但是很多应用程序架构基础性的功能却是大同小异,例如认证.授权.请求验证.异常处理. ...
- Python动态网页爬虫-----动态网页真实地址破解原理
参考链接:Python动态网页爬虫-----动态网页真实地址破解原理
- python 动态生成变量名以及动态获取变量的变量名
前言需求: 必须现在需要动态创建16个list,每个list的名字不一样,但是是有规律可循,比如第一个list的名字叫: arriage_list_0=[],第二个叫arriage_list_1=[] ...
随机推荐
- Redis 高可用方案
本文分享自天翼云开发者社区<Redis 高可用方案>,作者:芋泥麻薯 一.常见使用方式 Redis的几种常见使用方式包括: Redis单副本: • Redis多副本(主从): • Redi ...
- 深入理解Hadoop读书笔记-3
背景 公司的物流业务系统目前实现了使用storm集群进行过门事件的实时计算处理,但是还有一个需求,我们需要存储每个标签上传的每条明细数据,然后进行定期的标签报表统计,这个是目前的实时计算框架无法满足的 ...
- 【Linux】5.5 Shell运算符
Shell运算符 Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符 关系运算符 布尔运算符 字符串运算符 文件测试运算符 原生bash不支持简单的数学运算,但是可以通过其他命令来实现 ...
- D常用快捷键大全(转)
Ctrl+PageUp将光标移至本屏的第一行,屏幕不滚动.Ctrl+PageDown将光标移至本屏的最后一行,屏幕不滚动.Ctrl+↓向下滚动屏幕,光标跟随滚动不出本屏.Ctrl+↑向上滚动屏幕,光标 ...
- MCP技术:渗透测试从自动化到智能化
在人工智能快速发展的今天,如何让AI更高效地与现实世界交互,成为许多开发者和研究者关注的焦点.MCP(Model Context Protocol)技术作为一种创新的工具集成方案,为AI提供了一种&q ...
- Dubbo 中的集群容错
前言 在微服务架构中,服务间的依赖关系复杂且动态,任何一个服务的故障都可能引发连锁反应,导致系统雪崩.一个好的容错设计可以避免这些问题发生: 服务雪崩效应:单个服务崩溃或响应延迟可能导致调用链上的所有 ...
- k8s部署dify详细过程
一.概述 dify官方提供的安装方式是docker-compose方式部署的,单机运行. 但是在企业生产环境,单机没法提供冗余,一旦故障,就很麻烦了. 如果有大量的APP用户,那么单机承受不住这么多并 ...
- 在 Ubuntu 上安装 Python 3.11、创建虚拟环境并设置 pip 阿里云源的主要命令
在 Ubuntu 上安装 Python 3.11.创建虚拟环境并设置 pip 阿里云源的主要命令流程如下: 安装 Python 3.11 sudo apt update sudo apt instal ...
- Asp.net core基础(一)Entity FrameworkCore的增删查改
一.EntityFramework Core的介绍 EntityFramework Core是.net core中的ORM(object relational mapping[对象关系映射])框架,它 ...
- eolinker内置变量更新导致的脚本变化(适用于所有应用前置、后置变量的场景)
内置变量变化情况内置变量变化情况(相关地址https://help.eolinker.com/#/tutorial/?groupID=c-579&productID=13) 通过下表可以了解内 ...