是否可以将Python类/函数安排到包命名空间中,从而避免使用模块命名空间?

时间:2017-03-28 18:45:39

标签: python

让我们说我的文件结构如下:

.
├── a
│   ├── b
│   │   ├── ClassB0.py
│   │   ├── ClassB1.py
│   │   ├── functionB.py
│   │   └── __init__.py
│   ├── ClassA0.py
│   ├── ClassA1.py
│   ├── functionA.py
│   └── __init__.py
└── test.py

这个想法是每个文件包含一个类或一个函数 - 例如ClassB0.pyclass ClassB0: ...functionA.pydef functionA(): ...。但是,我希望能够使用前缀访问这些类/函数,但只能访问与文件夹相关的前缀。所以我希望能够在这个树的任何地方做任何类似的事情(假设Python始终在顶级文件夹中启动):

a.b.functionB()
a.b.ClassB1()
a.ClassA1()
a.functionA()

我想避免使用文件夹和文件的前缀:

a.b.functionB.functionB()
a.b.ClassB1.ClassB1()
a.ClassA1.ClassA1()
a.functionA.functionA()

我已尝试将以下代码放在__init__.py文件中

from .ClassB0 import ClassB0
from .ClassB1 import ClassB1
from .functionB import functionB

这适用于一个“级别”(因此没有b/)或非常简单的类。但是扭曲是ClassB1需要ClassB0并且这会通过引起循环依赖来打破整个事情(至少我认为这是我问题的根本原因)。如果我尝试运行我的脚本,我会收到这样的错误:

$ python test.py 
Traceback (most recent call last):
  File "test.py", line 1, in <module>
    import a
  File "/home/freddie/test/testpy/a/__init__.py", line 3, in <module>
    from .functionA import functionA
  File "/home/freddie/test/testpy/a/functionA.py", line 1, in <module>
    import a.b
  File "/home/freddie/test/testpy/a/b/__init__.py", line 2, in <module>
    from .ClassB1 import ClassB1
  File "/home/freddie/test/testpy/a/b/ClassB1.py", line 3, in <module>
    class ClassB1(a.b.ClassB0):
AttributeError: module 'a' has no attribute 'b'

作为参考,这是这些文件的完整代码。

test.py

import a

a.functionA()

a/ClassA0.py

class ClassA0:
    def __init__(self):
        print('ClassA0')

a/ClassA1.py

import a

class ClassA1(a.ClassA0):
    def __init__(self):
        a.ClassA0.__init__(self)
        print('ClassA1')

a/functionA.py

import a.b
import a

def functionA():
    a.ClassA1()
    a.b.functionB()

a/__init__.py

from .ClassA0 import ClassA0
from .ClassA1 import ClassA1
from .functionA import functionA

a/b/ClassB0.py

class ClassB0:
    def __init__(self):
        print('ClassB0')

a/b/ClassB1.py

import a.b

class ClassB1(a.b.ClassB0):
    def __init__(self):
        a.b.ClassB0.__init__(self)
        print('ClassB1')

a/b/functionB.py

import a.b

def functionB():
    a.b.ClassB1()

a/b/__init__.py

from .ClassB0 import ClassB0
from .ClassB1 import ClassB1
from .functionB import functionB

有什么方法可以解决这个问题(不使用Python导入系统中的低级别黑客攻击)?也许我已经习惯了C ++命名空间的工作方式,而这在Python中无法完成?

正如我之前所写,我正在尝试使用一个允许我使用完全限定名称的结构,但没有模块的名称,所以只有包名称。我还想避免将我的所有类/函数重命名为A_B_ClassB1,因为这看起来并不好(;我不是Python专家,所以也许我使用的文件结构或我尝试的方法(导入__init__.py文件)并不好 - 我愿意接受任何允许我使用a.b.ClassB1这样的名字的建议,但是有某种形式保留类似的组织在文件夹/文件级别。

2 个答案:

答案 0 :(得分:2)

你有一个好主意,但你忽略了Python所构建的功能。

Python包是以某种方式相关的模块的集合。

Python模块是一个单独的文件,其中包含一组一起工作的类和/或函数。

每个文件不应该有类或函数。那不是好设计。

答案 1 :(得分:1)

试图弄乱导入系统以使a.b.ClassB1引用a/b/ClassB1文件中的类是混乱的业务。它不是导致错误的直接原因,但它引入了许多其他复杂情况,并且通常难以解决问题。

我建议至少为类和文件命名不同的东西,比如_ClassB1.py文件和ClassB1类,这样你就可以单独引用类和模块而不会产生歧义。这很重要。

a/b/ClassB1.py尝试引用a.b.ClassB0时,a.b模块的模块对象实际上并未分配给b属性a 1}}模块对象。只有在__init__.py a.b完成执行且__init__.py仍在继续时才会发生这种情况。在a.b属性分配发生之前,您无法引用a.b

除了属性分配问题,ClassB1.py还假设a.b.ClassB0是类而不是模块,这意味着它假设from .ClassB0 import ClassB0出现在{from .ClassB1 import ClassB1之前在__init__.py中的1}}。这引入了导入排序要求,如果a.b问题没有先发生,会导致其他令人讨厌的麻烦。

要解决此问题,您可以使用相对导入来访问同级模块及其内容,而无需尝试访问a.b属性。不幸的是,这会让您的结构更加混乱,因为from . import ClassB0ClassB1.py之类的含义取决于__init__.py是否执行了相同的导入。如果您将文件名更改为与类名不冲突,则事情就会变得更加混乱:

# _ClassB1.py
from ._ClassB0 import ClassB0

class ClassB1(ClassB0):
    ...