簡體   English   中英

相對 position 和 Python 中命名空間包的內容 >= 3.3

[英]relative position and content of namespace packages in Python >= 3.3

我閱讀了文檔和相當多的 stackoverflow 帖子,但沒有找到明確的答案來解決我的疑問。

我想我了解命名空間包的用途。

我只對 Python>=3.3 和隱式命名空間包感興趣 - 沒有__init__.py的文件夾。

問題

  1. 命名空間包是否應該只包含其他包,或者模塊(即.py文件)也“允許”?

  2. 命名空間包是否應該僅用作“容器”包,或者它們也可以包含在常規包中

  3. 如果命名空間包僅作為容器有意義,我想我可以 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/childdir2/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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM