首页 > 解决方案 > 从另一个模块修改一个模块的属性是不好的做法吗?

问题描述

我想定义一堆可以在我项目的所有模块中导入的配置变量。这些变量的值在运行时将保持不变,但在运行前是未知的;它们取决于输入。通常我会在我的顶级模块中定义一个字典,它将传递给来自其他模块的所有函数和类;但是,我认为简单地创建一个空白的config.py模块可能会更干净,该模块将由顶部模块动态填充配置变量:

# top.py
import config
config.x = x

# config.py
x = None

# other.py
import config
print(config.x)

我喜欢这种方法,因为我不必将参数保存为其他模块中的类属性;这对我来说很有意义,因为参数本身并不描述类。

这行得通,但它被认为是不好的做法吗?

标签: python

解决方案


这样的问题可能会引起争议。但我通常会说是的,这是“不好的做法”,因为变化的范围和影响真的越来越模糊。请注意,您描述的用例实际上不是关于共享配置,而是关于程序功能、对象、模块交换数据的不同部分,因此它是(元)global变量的一些变体)。

读取通用配置值可能很好,但在此过程中更改它们......您可能会忘记在导入模块/修改值时发生的位置和顺序。例如假设config.py和 两个模块m1.py

import config
print(config.x)
config.x=1

m2.py

import config
print(config.x)
config.x=2

和一个main.py刚刚做的:

import m1
import m2
import config
print(config.x)

或者:

import m2
import m1
import config
print(config.x)

您在每个模块中以及实际上在任何其他模块(包括此处)中找到配置的状态main.py取决于导入发生的顺序以及谁在何时分配了什么值。即使对于一个完全在你控制之下的程序,这也可能会很快变得混乱(和错误的根源)。

对于运行时数据以及在对象和模块之间传递信息(您的示例实际上是这样,而不是在模块之间预定义和共享的配置),我建议您考虑在自定义状态(配置)对象中描述信息并传递它通过适当的接口。但实际上可能只需要一个函数/方法参数。确切的形式取决于您想要实现的具体目标以及您的整体设计。


在您的示例中,other.py之前调用或导入时的行为不同,top.py在最小示例中看起来仍然很明显且易于管理,但实际上并不是一个非常合理的设计。任何阅读代码的人(包括未来的你)都应该能够遵循它的逻辑,而这个 IMO 打破了它的流程。

关于您所描述的内容的最琐碎(和程序)示例,现在我希望能更好地掌握将重新other.py创建您当前的行为:

def do_stuff(value):
    print(value)  # We did something useful here

if __name__ == "__main__":
   do_stuff(None)  # Could also use config with defaults

top.py可能是入口点并协调导入和执行:

import other
x = get_the_value()
other.do_stuff(x)

您当然可以引入一个接口来配置do_stuff一个dict或一个自定义类,即使默认实现config.py如下:

class Params:
    def __init__(self, x=None):
        self.x = x

和你的other.py

def do_stuff(params=config.Params()):
    print(params.x)  # We did something useful here

在您的身上,top.py您可以使用:

params = config.Params(get_the_value())
other.do_stuff(params)

但是您也可以有任何特定于用例的值来源:

class TopParams:
    def __init__(self, url):
        self.x = get_value_from_url(url)

params = TopParams("https://example.com/value-source")
other.do_stuff(params)

x甚至可以是property您每次访问它时检索的...或者在需要时懒惰地然后缓存...同样,这实际上是您需要做什么的问题。


推荐阅读