Python 个子包和命名空间

Python subpackages and namespaces

我正在努力解决在我看来是一个非常基本和常见的问题,经过数小时的互联网搜索后我找不到任何答案这一事实告诉我,我一定是做错了什么...

我只是想找到一种优雅的方式来处理我的包裹的进口。

背景:

我的包裹结构如下:

mypackage/
    __init__.py
    model/
        __init__.py
        A.py
        B.py
    controllers/
        __init__.py
        A.py
        B.py

# mypackage/model/A.py
class A:
    def __init__(self):
        print("This is A's model.")
# mypackage/model/B.py
from mypackage.model.A import A as AModel
class B:
    def __init__(self):
        self._a_model = AModel()
        print("This is B's model.")

# mypackage/controllers/A.py
class A:
    def __init__(self):
        print("This is A's controller.")
# mypackage/controllers/B.py
from mypackage.controllers.A import A as AController
class B:
    def __init__(self):
        self._a = AController()
        print("This is B's controller.")

问题:

有两件事真的困扰着我这个设计。

第一:我想使用命名空间

我真的不喜欢写作

from mypackage.controllers.A import A as AController
...
self._a = AController()

感觉很麻烦,也不是很pythonic... 我更喜欢使用名称空间,如 :

from mypackage import controllers
...
self._a = controllers.A.A()

但如果我尝试,我会得到 AttributeError: module 'mypackage.controllers' has no attribute 'A'

其次:我真的不喜欢输入 class 的文件名

我真的不喜欢写作

from mypackage.controllers.A import A as AController

我更愿意:

from mypackage.controllers import A as AController

什么没用

将所有内容放在一个文件中

我知道我可以通过将所有控制器的 class(A 和 B)定义放在一个文件中(controllers.py)来获得我想要的东西,并对模型执行相同的操作...

我多次读到,在 python 中将几个 class 定义放在一个文件中是很常见的事情...但是对于大的单独 classes 我只是不能。如果 A 和 B 是数百行并且彼此无关(除了作为控制器和模型),将它们的定义放在一个文件中是不可用的。

init.py 文件中使用导入

的确,它会解决我所有的问题...

除了:

  1. 这会导致循环导入。如您所见,我需要在 B 的模型中使用 A 的模型...因此,如果在我需要访问其中一个模型时导入所有模型,我陷入了恶性循环...
  2. 看起来不是很pythonic。如果只是因为它强制用户加载每个模块。

我在这里...

我的推理有什么问题?

# mypackage/controllers/__init__.py
from A import A

然后你可以在 mypackage 之外创建一个新文件。

# check.py
from mypackage.controllers import A as AController
from mypackage import controllers
a = controllers.A()
>>> This is A's controller.

让我们知道它是否适合您。

[我脑子里乱七八糟的,没有测试]

I really don't like writing

from mypackage.controllers.A import A as AController
#...
    self._a = AController()

It feels cumbersome and not very pythonic... I would prefer using namespaces like in :

from mypackage import controllers
# ...
    self._a = controllers.A.A()

mypackage/controllers/__init__.py 你需要:from . import A.

I really don't like writing

from mypackage.controllers.A import A as AController

I would prefer :

from mypackage.controllers import A as AController

mypackage/controllers/__init__.py 你需要 from A import A.

Using imports in the __init__.py file

这就是要走的路。 对不起,如果它看起来对你来说太多样板,但如果你的项目很大,那就是需要的。

至于循环导入问题:它会在你写你的导入时起作用 是否在 __init__ 文件中,这只是一个后勤问题。 如果在你的 __init__ 文件中,你以正确的顺序写入导入,就不会有循环问题。

即在你的 myproject/models/__init__.py 中你有:

from .A import A as AModel
from .B import B as BModel

当然,您将 .py 文件命名为与 classes 相同的名称对您没有帮助 - 如果您至少放弃文件名中的大小写,您可以这样写:

from .a import A
from .b import B

否则,你可以这样做:

import myproject.models.A
import myproject.models.B

A = myproject.models.A.A
B = myproject.models.B.B

为了能够将“myproject.models.A”用作 class: __init__ 中的名称 A 将覆盖模块对象 同名

一个写作 import myproject.models.A 将到达模块,但是通过执行 from myproject.models import A 你得到了模块。

如果这让人感到困惑...尽量不要对模块文件使用与 classes 相同的名称。即使在忽略大小写的文件系统中,比如 Windows 你会 总之模棱两可。坚持约定:snake_case 中的模块名称,CamelCase

中的 class 名称

回到循环导入问题:关键是在这个设计中,b.py只是在a.py已经导入之后才被读取,没有循环导入问题。

这并不总是可能的 - 有时需要子模块之间的交叉引用。在这些情况下可以做的是将 import 行移动到 函数或方法,而不是作为全局语句。这样,当执行导入时,引用的模块已经初始化。

在你的例子中是:

mypackage.models.b.py

# mypackage/model/B.py

class B:
    def __init__(self):
        from mypackage.model.A import A as AModel
        self._a_model = AModel()
        print("This is B's model.")