【问题标题】:Python `__init__.py` and initialization of objects in a codePython `__init__.py` 和代码中对象的初始化
【发布时间】:2012-03-22 14:15:22
【问题描述】:

我已经阅读了关于__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()

然后,在我需要使用 Aliase 的任何文件中,我都会这样做:

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 解决方案,或可能使用户感到困惑或使事情变得不安全的解决方案。

【问题讨论】:

    标签: python module initialization package


    【解决方案1】:

    您在模块中内联创建单个实例的做法很好。使用全局变量没有任何问题(除非它们不合适,但这适用于任何语言特性)。唯一潜在的缺点是如果您将导入该模块而不使用它并浪费资源初始化该类。除非你的班级在创建时做了一些非常重的事情,我非常怀疑,否则这种影响可以忽略不计。我唯一建议的是你调整你的命名。您的类可能应该以下划线开头,以便您的模块的用户知道不要触摸它,并且您的实例应该是小写的。您使用的方法不会造成任何不良副作用。

    没有人说您必须将所有类放在自己的文件中,或者您必须使用类。也许一个包含函数的别名模块和模块全局变量中的“私有”状态对你来说更有意义。由于您没有使用对象系统来获取具有独立状态的多个实例,因此创建类的唯一原因是您是否更喜欢组织代码和 API。这里最重要的是您对代码的组织方式和模块的使用方式感到满意。使用太多的面向对象技术比使用太少的 imo 更有可能使用户感到困惑。只需确保在您确实需要课程时进行课程。

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

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

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

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

    【讨论】:

    • 非常感谢文森特这个长而有用的回答。尽管 Borg 模式很有趣,但我会坚持使用更“传统”的解决方案,并尝试遵循您的好建议。
    【解决方案2】:

    使用Borg pattern 在整个代码中实现所有共享类,然后将它们存储在它们自己的模块中(或捆绑在一起),只要看起来合适。

    然后只需在需要的地方导入它们并实例化类。它们都将共享相同的上下文,您甚至可以微调每个实例化实例的行为。

    如果它们在自己的模块中被隔离,那么您将不会有任何循环依赖,并且您将在导入期间最大限度地减少解释器的解析时间。最好的是,您不会通过在其他地方导入全局变量来污染您的命名空间。当您的函数超出范围时,实例将被标记为删除,但状态将与类定义一起保留。

    【讨论】:

    • 非常感谢您的回答!但是如果__getattribute__(或类似的方法)被重新定义,这是一个问题吗?另外,我正在使用多处理,它是具有 Borg 模式的精细酸洗类吗?无论如何,谢谢你的提示,我不知道 Borg 模式。
    • 酸洗或覆盖__getattribute__ 不应该有任何问题,尽管通常我建议你覆盖__getattr__,除非你有充分的理由这样做。与任何其他类相比,Borg 类并没有什么特别之处。就效率而言,如果你腌制 borg 类,你显然只需要这样做一次。
    • 非常感谢。我想我不会选择这个作为我的解决方案,但我很高兴我现在意识到了这一点。你最后的评论让我更感兴趣。我当然会尝试这样的事情,看看我是否能从中受益。
    猜你喜欢
    • 2015-11-08
    • 2011-11-07
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多