Python: 无状态函数库设计
我正在建立一个包含各种常用功能的库,这些功能在项目中会经常被重复使用。每个功能都是无状态的,也就是说它们在创建时不需要参数,也没有任何记忆。某些功能会调用其他功能。
这些功能会在项目的其他部分作为参数传递。
那么,以下哪种方法更好呢?
1. 在某个模块中将所有功能定义为全局函数:
def f1(x):
# use x
def f2(x):
# use x and f1
2. 将所有功能定义为类中的方法,并根据使用情况将类安排成一个层次结构:
class F1:
def __call__(x):
# use x
f1 = F1()
class F2(F1):
def __call__(x):
# use x and f1
f2 = F2()
我之所以考虑第二种选择,是因为我的一些功能有共同之处。例如,功能 f2
、f3
和 f11
都是先调用 f1
。我在想我可能想要这样做:
class F1:
def __call__(self, x):
self.f1(x)
self.calc(x)
def f1(self, x):
# do something
# don't define calc here; F1 is abstract base class
class F2(F1):
def calc(self, x):
# do something
class F3(F1):
def calc(self, x):
# do something
3 个回答
2
如果你的类里没有存储数据,也就是说这些类是无状态的函数,那我觉得用它们没什么意义。Python 允许你在模块级别定义函数,这样你就不必非得使用类,除非你真的需要类的一些功能。
3
如果你有几个函数需要在开始和/或结束时执行一些相同的代码,你可以把这些共同的代码放在一个装饰器里,具体内容可以参考这里的解释。
3
选项1简单得多,而选项2则复杂得没有必要!
还有一个建议,可以让测试变得更简单:
1.1. 把它们都定义为一个模块中的单个类的方法。根据需要使用@staticmethod和@classmethod这两个装饰器。这样做可以让你更容易用模拟对象替换它们,或者通过提供一个新类或子类来覆盖它们。
spam.py:
class Spam(object):
@staticmethod
def f1(x):
# use x
@classmethod
def f2(cls, x):
# use x and cls.f1
不过,这样做还是比较复杂,所以你可能想先坚持使用选项1,直到你真的需要上面的那些功能。