将同类算法函数放入空类作为容器是否为不良编程实践?
我有几个完成相同任务但采用不同算法的函数,因为这些算法同属一个父流程,就把它们放到了my_package包下my_empty_class.py模块里的空类my_empty_class中当方法用,代码如下:
class my_empty_class: """Define an empty class to hold the methods""" def func1(param1, param2, param3): # algo 1 return results def func2(param1, param3): # algo 2 return results def func3(param2, param4): # algo 3 return results
包的文件结构是这样的:
my_package/ __init__.py my_empty_class.py another_module.py onemore_module.py ...
__init__.py里的内容:
from .my_empty_class import my_empty_class as my_empty_class from .another_module import another_module as another_module from .onemore_module import onemore_module as onemore_module ...
调用的时候是这么用的:
import my_package res1 = my_package.my_empty_class.func1(param1, param2, param3) res2 = my_package.my_empty_class.func2(param1, param3) res3 = my_package.my_empty_class.func3(param2, param4)
这么做是能跑,但总感觉不太规范,有没有更推荐的实现方式?
1. 直接用模块级函数(最简洁的方案)
你现在用空类只是当个“容器”装函数,完全没必要多这一层类,直接把函数放到模块里就行——甚至可以把my_empty_class.py改个更有意义的名字,比如algorithms.py:
# my_package/algorithms.py def func1(param1, param2, param3): # algo 1 return results def func2(param1, param3): # algo 2 return results def func3(param2, param4): # algo 3 return results
然后在__init__.py里导出这些函数(或者保留模块名,看你喜欢哪种调用方式):
# 方式一:直接导出函数,调用时不用写模块名 from .algorithms import func1, func2, func3 # 方式二:保留模块名,强调分组 from .algorithms import algorithms as algorithms
调用的时候就更清爽了:
import my_package # 方式一的调用 res1 = my_package.func1(param1, param2, param3) # 方式二的调用 res1 = my_package.algorithms.func1(param1, param2, param3)
这完全符合Python“扁平结构优于嵌套”的设计原则,没必要为了分组硬套一个空类。
2. 用子包做命名空间(适合算法逻辑复杂的情况)
如果每个算法的代码量都很大,不想挤在一个文件里,可以专门建个algorithms子包,把每个算法拆到单独文件里:
my_package/ __init__.py algorithms/ __init__.py func1.py func2.py func3.py another_module.py ...
然后在algorithms/__init__.py里统一导出所有算法:
from .func1 import func1 from .func2 import func2 from .func3 import func3
调用方式和模块级函数差不多:
import my_package res1 = my_package.algorithms.func1(...)
这种结构更清晰,后期维护单个算法也方便。
3. 抽象基类(需要规范算法接口时用)
如果你希望所有算法都遵循统一的输入输出规范,避免后续加新算法时乱写签名,可以用abc模块定义一个抽象基类,让每个算法类继承并实现接口:
from abc import ABC, abstractmethod class BaseAlgorithm(ABC): @abstractmethod def execute(self, **kwargs): pass class Algorithm1(BaseAlgorithm): def execute(self, param1, param2, param3): # algo 1 return results class Algorithm2(BaseAlgorithm): def execute(self, param1, param3): # algo 2 return results
调用的时候需要先实例化类:
algo1 = Algorithm1() res1 = algo1.execute(param1, param2, param3)
不过要是你的算法不需要保存任何状态(比如不需要实例变量),这种方式就有点多余,不如模块级函数简洁。
4. 策略模式(需要动态切换算法时用)
如果你的业务逻辑需要在运行时根据不同情况切换算法,可以用策略模式,搞个上下文类来管理算法:
class AlgorithmContext: def __init__(self, strategy): self.strategy = strategy def run(self, **kwargs): return self.strategy(**kwargs) # 先定义好各个算法函数 def func1(param1, param2, param3): return results def func2(param1, param3): return results # 使用示例 context = AlgorithmContext(func1) res1 = context.run(param1=..., param2=..., param3=...) # 切换算法 context.strategy = func2 res2 = context.run(param1=..., param3=...)
这种方式适合需要灵活切换算法的场景,比如用户选不同的计算方式时动态调整。
内容的提问来源于stack exchange,提问作者Gabriel

