python - 从另一个模块修改一个模块的属性是不好的做法吗?
问题描述
我想定义一堆可以在我项目的所有模块中导入的配置变量。这些变量的值在运行时将保持不变,但在运行前是未知的;它们取决于输入。通常我会在我的顶级模块中定义一个字典,它将传递给来自其他模块的所有函数和类;但是,我认为简单地创建一个空白的config.py模块可能会更干净,该模块将由顶部模块动态填充配置变量:
# top.py
import config
config.x = x
# config.py
x = None
# other.py
import config
print(config.x)
我喜欢这种方法,因为我不必将参数保存为其他模块中的类属性;这对我来说很有意义,因为参数本身并不描述类。
这行得通,但它被认为是不好的做法吗?
解决方案
这样的问题可能会引起争议。但我通常会说是的,这是“不好的做法”,因为变化的范围和影响真的越来越模糊。请注意,您描述的用例实际上不是关于共享配置,而是关于程序功能、对象、模块交换数据的不同部分,因此它是(元)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
您每次访问它时检索的...或者在需要时懒惰地然后缓存...同样,这实际上是您需要做什么的问题。
推荐阅读
- c# - 使用 C# 在 Outlook 365 插件中获取单独的名字和姓氏
- c - 我将如何防止不正确的访问?我将如何从仅比较顶行的字符串中迭代一个函数?
- php - Laravel `->whereDate` 不包括今天
- oracle - Oracle LPAD() 函数
- javascript - 允许模式以可选的特定字符开头但没有其他字符的正则表达式
- android - 我想让我的联系人和拨号器应用程序默认设置为颤振
- python - 如何使用 Python 在二进制状态下编辑视频元数据
- python - 如何让 fastavro 支持逻辑类型?
- python - 使用 Python 在上位词级别通过类比进行文本语义相似性
- python - 颤振/飞镖:在数据点之间使用梯度插值绘制二维图