[英]relative position and content of namespace packages in Python >= 3.3
我閱讀了文檔和相當多的 stackoverflow 帖子,但沒有找到明確的答案來解決我的疑問。
我想我了解命名空間包的用途。
我只對 Python>=3.3 和隱式命名空間包感興趣 - 沒有__init__.py
的文件夾。
命名空間包是否應該只包含其他包,或者模塊(即.py
文件)也“允許”?
命名空間包是否應該僅用作“容器”包,或者它們也可以包含在常規包中?
如果命名空間包僅作為容器有意義,我想我可以 state ,只要我有一個真正的 package 文件夾,它所有包含 python 模塊的子文件夾也應該有__init__.py
?
# this is fine
ns_package/
+-- real_package/
+-- __init.py__
# how about this?
real_package/
+-- __init.py__ # I have it for docs AND want to force the dir to be a real package
+-- ns_package/ # I would just like to avoid an empty __init__.py
+-- amodule.py
我懷疑命名空間包僅作為容器才有意義,因為在另一種情況下,我將無法使用不同路徑中的其他東西擴展命名空間,因為父級是一個真正的 package,必須在文件系統。 因此,我不會獲得命名空間包的主要優勢。
我之所以問,是因為在運行和導入模塊時(從項目的根目錄),在常規 package 內具有隱式命名空間 package 的情況可以正常工作。 但是,它需要對安裝腳本進行一些調整,我想知道我是否首先做了一些有缺陷的事情。
注意:我嘗試使用隱式命名空間包主要不是因為我想利用它們的功能,而是因為我討厭空的__init__.py
文件。 我最初以為 python 3.3 終於擺脫了這個,包不再需要__init__.py
了,但似乎沒有那么簡單......
首先:您使用命名空間包的動機是有缺陷的。 空的__init__.py
文件沒有問題; 它們現在可能是空的,但稍后可以填充內容。 即使它們保持空置也不會造成任何麻煩。
話雖如此,從技術上講,將命名空間 package 放在常規 package 中並沒有錯。 當您執行import abc
形式的導入時,每個組件都會單獨解析,並且b
可以是位於常規 ZEFE90A8E604A7C840E88D03A67F6B7D8 中a
命名空間 package。 考慮以下目錄布局:
.
└── a
├── b
│ └── c.py
└── __init__.py
然后你可以導入模塊c
:
>>> import a.b.c
>>> a
<module 'a' from '/tmp/a/__init__.py'>
>>> a.b
<module 'a.b' (namespace)>
>>> a.b.c
<module 'a.b.c' from '/tmp/a/b/c.py'>
如您所見,所有組件都是單獨實例化的,其中命名空間ab
的__file__
屬性設置為None
。
然而,這種設置阻止了命名空間包的主要用途,即它們可以拆分到多個目錄中。 這是因為即使b
是命名空間 package,它也存在於常規 package a
中,該名稱將緩存在sys.modules
,從而防止進一步搜索導入路徑。 例如,考慮以下目錄布局:
.
├── dir1
│ └── parent
│ ├── child
│ │ ├── one.py
│ ├── __init__.py
├── dir2
│ └── parent
│ ├── child
│ │ └── two.py
│ └── __init__.py
└── main.py
有兩個命名空間包dir1/parent/child
和dir2/parent/child
。 但是,您只能使用其中一個,因為常規的 package dir1/parent
會阻止訪問另一個。 讓我們為main.py
嘗試以下內容:
import sys
sys.path.extend(('dir1', 'dir1'))
import parent.child.one # this works
print(sys.modules['parent'])
print(sys.modules['parent.child'])
print(sys.modules['parent.child.one'])
import parent.child.two # this fails
我們將得到以下 output:
<module 'parent' from 'dir1/parent/__init__.py'>
<module 'parent.child' (namespace)>
<module 'parent.child.one' from 'dir1/parent/child/one.py'>
Traceback (most recent call last):
File "main.py", line 11, in <module>
import parent.child.two
ModuleNotFoundError: No module named 'parent.child.two'
這是因為sys.modules['parent']
是常規的 package,因此在import parent.child.two
中, parent
組件被解析為非常 package,它確實有一個屬性child
,但這個命名空間不包含two
. 需要進一步搜索導入路徑才能找到該模塊。
1)您可以在命名空間 package 層次結構的任何級別擁有.py
文件。 只要它不包含__init__.py
文件,它就被認為是一個命名空間 package 並相應地解析其內容。 考慮以下目錄布局:
.
└── a
├── b
│ ├── c
│ │ └── three.py
│ └── two.py
└── one.py
您可以在任何命名空間包中導入任何模塊:
>>> import a.one
>>> import a.b.two
>>> import a.b.c.three
>>> a.b.c
<module 'a.b.c' (namespace)>
2)如上所述,您可以將命名空間包放在常規包中,但這沒有多大意義,因為它會阻止它們的預期用途。
3)這在很大程度上取決於您所說的“應該”是什么意思。 從技術上講, __init__.py
不是必需的,但它確實很有意義。
如開頭所述, __init__.py
文件的目的不僅僅是指示常規的 python 包,而且它們通常也充滿了內容。 如果沒有,這沒什么好擔心的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.