Python: 无状态函数库设计

5 投票
3 回答
4444 浏览
提问于 2025-04-17 10:49

我正在建立一个包含各种常用功能的库,这些功能在项目中会经常被重复使用。每个功能都是无状态的,也就是说它们在创建时不需要参数,也没有任何记忆。某些功能会调用其他功能。

这些功能会在项目的其他部分作为参数传递。

那么,以下哪种方法更好呢?

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()

我之所以考虑第二种选择,是因为我的一些功能有共同之处。例如,功能 f2f3f11 都是先调用 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,直到你真的需要上面的那些功能。

撰写回答