让我们说我的文件结构如下:
.
├── a
│ ├── b
│ │ ├── ClassB0.py
│ │ ├── ClassB1.py
│ │ ├── functionB.py
│ │ └── __init__.py
│ ├── ClassA0.py
│ ├── ClassA1.py
│ ├── functionA.py
│ └── __init__.py
└── test.py
这个想法是每个文件包含一个类或一个函数 - 例如ClassB0.py
有class ClassB0: ...
而functionA.py
有def 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
这样的名字的建议,但是有某种形式保留类似的组织在文件夹/文件级别。
答案 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 ClassB0
内ClassB1.py
之类的含义取决于__init__.py
是否执行了相同的导入。如果您将文件名更改为与类名不冲突,则事情就会变得更加混乱:
# _ClassB1.py
from ._ClassB0 import ClassB0
class ClassB1(ClassB0):
...