Python`__init __.py`和代码中对象的初始化

我已经阅读了关于__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 / __ init__.py中导入的文件(例如root / shared.py)中,以便我确定它已启动?

我已多次阅读,最好将__init__.py文件保留为空(现在我的代码就是这种情况,当然除了root / __ init__.py).你怎么看?

我有点迷茫(你可能从我不太清楚的事实看出来).任何帮助/建议都非常受欢迎.我想避免任何非pythonic解决方案,或者可能使用户感到困惑或使事情变得不安全的解决方案.

最佳答案 你在模块中内联创建单个实例的过程就是FINE.使用全局变量没有任何问题(除非它们不合适,但适用于任何语言功能).唯一的潜在缺点是如果您在不使用它的情况下导入该模块并浪费资源来初始化该类.除非你的班级在创作时做了一些非常沉重的事情,我非常怀疑,这种影响可以忽略不计.我唯一建议的是你调整你的命名.您的类应该以下划线开头,因此模块的用户知道不要触摸它,并且您的实例应该是小写的.您使用的方法不会产生任何不良副作用.

没有人说你必须把你的所有课程放在他们自己的文件中,或者你必须要使用课程.也许一个带有函数的别名模块和模块全局变量中的“私有”状态对你来说更有意义.由于您没有使用对象系统来获取具有独立状态的多个实例,因此创建类的唯一原因是您希望如何组织代码和API.最重要的是,您对代码的组织方式以及模块的使用方式感到满意.通过使用面向对象的技术而不是太少的imo,你更容易混淆用户.当你确实需要一个课时,确保你上课.

单身人士还有很多其他方法.这是我第一次听说过“博格模式”,这对我来说听起来既浪费又愚蠢,但我想这至少在技术上是有效的,浪费至少可以忽略不计.您可以编写一个函数,在第一次调用类时将其实例化,将其存储在全局中,并在后续调用中返回全局(但是您必须担心线程,这不是您已经在做的方式的问题)它).您可以创建一个__init__方法引发TypeError的类,以便它根本无法实例化,并使用classmethods或staticmethods完成所有操作,保留类本身所需的所有状态.甚至可以创建一个每次都返回相同实例的类.所有这些的结果是,您从模块中获取了一个对象,然后在您的代码中使用它,并且在此之后使用它的方式看起来是一样的.

我认为你应该只在你的一个模块中实例化你的类,就像你发布的代码一样,因为它有效,它是一种常见的模式,它是明智的,它对你有意义.

没有理由删除您导入的模块.它不会为你节省任何东西.该模块一旦导入就永远存在(除非你主动做一些奇怪的事情来摆脱它),而全局变量并不是你需要保存的宝贵资源.

只要在内部使用相对名称(即别名而不是root.tools.aliases)引用模块,就不会通过在__init__.py中导入模块来引入循环依赖关系.循环依赖是一个真正的问题,所以要小心.只要您的模块只导入树中更深层的东西,并且您认为它们是“低级”(意味着那些较低级别的模块不会导入可能正在使用它们的更高级别的东西),你应该没事.我会提醒我不要在__init__.py中添加任何实质性代码,但我认为如果你希望实例存在,那么实例化你的类就没问题了.

点赞