我已经阅读了关于__init__.py
文件的文档和一些关于SO的好问题,但我仍然对它的正确用法感到困惑。
我有一个包含许多包和子包的代码。我已经定义了许多类,其中一些我需要为整个用户会话创建一个(并且只有一个)实例。然后,这些新对象将在代码的不同部分中使用,这样,无论何时我(或用户)更新此对象中的数据/信息,它都将在所有代码中使用,而无需更改任何其他内容。 为了更清楚,让我告诉你一个我正在谈论的基本方案。
代码的结构过于简单,如:
root/
__init__.py
tools/
__init__.py
... (some modules)
aliases.py (which defines the class Aliases)
physics/
__init__.py
... (some modules)
constants/
... (some modules)
constants.py (which defines the class Ctes)
units.py (which defines the class Units)
在代码中,我需要管理别名,单位和常量。我发现处理它的方法是创建每个实例,并在所有代码中使用它。用这种方法, 我确信,例如,如果在代码仍在运行时添加了别名,则可以在代码中的任何位置使用它,因为只有一个别名的共享实例。 这就是我需要的东西(同样适用于单位,顺便说一下常量)。
至于现在,我认为这样做的方式并不是最好的。实际上,我在同一个文件中创建了一个实例,比如说别名,就是在声明类之后:
root/tools/aliases.py
中的
import ... (stuff
class Aliases(object):
"""The Aliases manager"""
...
ALIASES = Aliases()
然后,在我需要使用别名的任何文件中,我这样做:
any_file.py
中的(代码中的任何位置)
from root.tools.aliases import ALIASES
ALIASES.method1() # possibly in functions or other classes
ALIASES.method2() # ... etc
对于其他一些类,我甚至在代码的根目录中使用__init__.py
文件:
root/__init__.py
中的
# CTES is the instance of Ctes() created in root/physics/constants/constants.py
from root.physics.constants.constants import CTES
CTES.add(...) # add a new constant that needs to be known
(当然,CTES不只是存储一些常量,我定义了一些方法来利用它们,所以将它们放在这个类中而不是仅仅将它们定义为常规python是有意义的 模块中的常量)
我想知道我是否正确行事(可能不行)。也许最好使用文件__init__.py
并在其中启动共享实例。但是这会带来一些问题(如依赖循环,或增加内存使用......)?另外,如何在代码中的其他地方使用创建的实例?像这样?:
root/tools/__init__.py
中的
import root.tools.aliases as Al
ALIASES = Al.Aliases()
# should I delete the imported module: del Al ??
然后在any_file.py
from root.tools import ALIASES
ALIASES.method(...)
或者是否应将所有这些实例更好地包含在root/shared.py
中导入的文件中(例如root/__init__.py
),以便我确定它已启动?
我已多次阅读,最好将__init__.py
个文件保留为空(现在我的代码就是这种情况,当然除了root/__init__.py
)。你觉得怎么样?
我有点迷失(你可能从我不太清楚的事实看出来)。任何帮助/建议都非常受欢迎。我想避免任何非pythonic解决方案,或者可能使用户感到困惑或使事情变得不安全的解决方案。
答案 0 :(得分:1)
使用Borg pattern在整个代码中实现所有共享类,然后将它们存储在自己的模块中(或捆绑在一起),只要它们看起来合适。
然后只需在需要的地方导入它们并实例化该类。它们将共享相同的上下文,您甚至可以微调每个实例的实例行为。
如果它们在自己的模块中被隔离,那么您将不会有任何循环依赖关系,并且您将在导入期间最小化解释器的解析时间。最好的是,您不会通过在其他位置导入全局变量来污染您的命名空间。当您的函数超出范围时,实例将被标记为删除,但状态将保留为类定义。
答案 1 :(得分:1)
您在模块中内联创建单个实例的操作很精细。使用全局变量没有任何问题(除非它们不合适,但适用于任何语言功能)。唯一的潜在缺点是如果您在不使用它的情况下导入该模块并浪费资源来初始化该类。除非你的班级在创作时做了一些非常沉重的事情,我非常怀疑,这种影响可以忽略不计。我唯一建议的是你调整你的命名。您的类应该以下划线开头,因此模块的用户知道不要触摸它,并且您的实例应该是小写的。您使用的方法不会产生任何不良副作用。
没有人说你必须将所有课程都放在他们自己的文件中,或者你必须使用课程。也许一个带有函数的别名模块和模块全局变量中的“私有”状态对你来说更有意义。由于您没有使用对象系统来获取具有独立状态的多个实例,因此创建类的唯一原因是您希望如何组织代码和API。最重要的是,您对代码的组织方式以及模块的使用方式感到满意。通过使用面向对象的技术而不是太少的imo,你更容易混淆用户。当你确实需要一个课时,请确保你上课。
还有很多其他的单身人士方法。这是我第一次听说过“博格模式”,这对我来说听起来既浪费又愚蠢,但我想这至少在技术上是有效的,浪费至少可以忽略不计。您可以编写一个函数,在第一次调用类时将其实例化,将其存储在全局中,并在后续调用中返回全局(但是您必须担心线程,这不是您已经在做的方式的问题)它)。您可以创建一个__init__
方法引发TypeError的类,以便它根本无法实例化,并使用classmethods或staticmethods执行所有操作,保留类本身所需的所有状态。甚至可以创建一个每次都返回相同实例的类。所有这些的结果是,您从模块中获取了一个对象,然后在代码中使用该对象,之后使用它的方式看起来一样。
我认为你应该只在你的一个模块中实例化你的类,就像你发布的代码一样,因为它有效,它是一种常见的模式,它是明智的,对你来说很有意义。
没有理由删除您导入的模块。它不会为你节省任何东西。该模块一旦导入就永远存在(除非你积极做一些奇怪的事情来摆脱它),而全局变量并不是你需要保存的宝贵资源。
只要在内部使用相对名称(即别名而不是root.tools.aliases)引用模块,就不会通过导入__init__.py,
内的模块来引入循环依赖。循环依赖是一个真正的问题,所以要小心。只要您的模块只导入树中更深层的东西,并且您认为它们是“低级”(意味着那些较低级别的模块不会导入可能正在使用它们的更高级别的东西),你应该没事。我会提醒我不要在__init__.py,
中添加任何实质性代码,但我认为如果你希望实例存在,那么实例化你的类就没问题了。