我即将重构一个基于twisted 构建的python 项目的代码。到目前为止,我一直在使用一个简单的 settings.py
模块来存储常量和字典,例如:
#settings.py
MY_CONSTANT='whatever'
A_SLIGHTLY_COMPLEX_CONF= {'param_a':'a', 'param_b':b}
大量模块导入 settings.py
来完成它们的工作。
我想重构项目的原因是因为我需要动态更改/添加配置参数。我即将采取的方法是将所有配置收集在一个单例中,并在需要时访问其实例。
import settings.MyBloatedConfig
def first_insteresting_function():
cfg = MyBloatedConfig.get_instance()
a_much_needed_param = cfg["a_respectable_key"]
#do stuff
#several thousands of functions later
def gazillionth_function_in_module():
tired_cfg = MyBloatedConfig.get_instance()
a_frustrated_value = cfg["another_respectable_key"]
#do other stuff
这种方法有效,但感觉不Python且臃肿。另一种方法是外部化模块中的 cfg 对象,如下所示:
CONFIG=MyBloatedConfig.get_instance()
def a_suspiciously_slimmer_function():
suspicious_value = CONFIG["a_shady_parameter_key"]
不幸的是,如果我更改另一个模块中的 MyBloatedConfig
实例条目,这将不起作用。由于我使用的是 react 器模式,因此将人员存储在本地线程上以及使用队列都是不可能的。
为了完整起见,以下是我用来实现单例模式的实现
instances = {}
def singleton(cls):
""" Use class as singleton. """
global instances
@wraps(cls)
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class MyBloatedConfig(dict):
....
还有其他更Pythonic的方式来跨不同模块广播配置更改吗?
最佳答案
大的全局(通常是单例)配置对象是一种反模式。
无论您有 settings.py
、MyBloatedConfig.get_instance()
风格的单例,还是您在此处概述的任何其他方法,您都可以基本上使用相同的反模式。确切的拼写并不重要,这些只是让整个项目中的所有代码共享真正的全局(与 Python 模块级全局不同)的方法。
出于多种原因,这是一种反模式:
- 它使您的代码难以进行单元测试。任何基于此全局更改其行为的代码都将需要某种黑客攻击(通常是猴子修补),以便让您在不同配置下对其行为进行单元测试。将此与编写为接受参数(如函数参数)并根据传递给它的值改变其行为的代码进行比较。
- 它会降低您的代码的可重用性。由于配置是全局的,因此如果您想在两种不同的配置下使用依赖于该配置对象的任何代码,您将不得不跳过重重困难。你的单例只能代表一种配置。因此,您必须来回交换全局状态才能获得您想要的不同行为。
- 这会让你的代码更难理解。如果您查看一段使用全局配置的代码并且想知道它是如何工作的,则必须查看配置。然而,比这更糟糕的是,如果您想更改配置,则必须查看整个代码库以找到可能影响的任何代码。这会导致配置随着时间的推移而增长,因为您向其中添加新项目,并且很少删除或修改旧项目,因为担心会破坏某些内容(或者没有时间正确跟踪旧项目的所有用户)。<
上述问题应该会提示您解决方案是什么。如果您有一个函数需要知道某个常量的值,请使其接受该值作为参数。如果您有一个需要大量值的函数,那么创建一个类,可以将这些值包装在一个方便的容器中,并将该类的实例传递给该函数。
这个解决方案中经常困扰人们的部分是他们不想花时间输入所有传递的参数。以前的函数可能需要一个或两个(甚至零个)参数,而现在您的函数可能需要三个或四个参数。如果您正在转换以 settings.py
样式编写的应用程序,那么您可能会发现某些函数使用了全局配置中的六个或更多项目,并且这些函数突然变得一个很长的签名。
我不会否认这是一个潜在的问题,但应该主要将其视为现有代码的结构和组织的问题。最终签名过长的函数依赖于之前的所有数据。事实只是被你掩盖了。与大多数向您隐藏程序的某些方面的编程模式一样,这是一件坏事。一旦您显式地传递所有这些值,您就会看到您的抽象需要在哪里工作。也许那个 10 个参数的函数做得太多了,作为三个不同的函数会更好地工作。或者您可能会注意到,其中一半参数实际上是相关的,并且始终作为容器对象的一部分属于在一起。也许您甚至可以将一些与这些参数操作相关的逻辑放到该容器对象上。
关于python - pythontwisted中动态广播配置变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14763307/