<address id="tl19n"></address>
      <address id="tl19n"><nobr id="tl19n"></nobr></address>

        <address id="tl19n"></address>

          <address id="tl19n"></address>
          <form id="tl19n"></form>

              首頁 > 上網技巧 > 命名空間包 Namespace package

              命名空間包 Namespace package

              時間:2022-01-11 12:38 作者:QQ地帶 我要評論

              我學習 Python 的包內容時只有常規包,也就是以一個包含__init__.py文件的目錄形式實現。以一個包含__init__.py文件的目錄形式實現:
               
              ❯ tree regular
              regular
              ├── __init__.py
              ├── a
              │   └── __init__.py
              └── b
                  └── __init__.py
              如果沒有這個__init__.py文件就會造成導入失敗 (python 2):
               
              ❯ rm regular/__init__.py
               
              ❯ ipython2
              Python 2.7.16 (default, Nov  9 2019, 05:55:08)
               
              In : import regular
              ---------------------------------------------------------------------------
              ImportError                               Traceback (most recent call last)
              <ipython-input-1-3dca75a44ca9> in <module>()
              ----> 1 import regular
               
              ImportError: No module named regular
               
              In : import regular.a
              ---------------------------------------------------------------------------
              ImportError                               Traceback (most recent call last)
              <ipython-input-2-2f312ff46378> in <module>()
              ----> 1 import regular.a
               
              ImportError: No module named regular.a
              這非常符合預期 (或者說,習慣了這種設定),不過本文說的是在 Python 3 中的效果:
               
              ❯ ipython3
              Python 3.7.1 (default, Dec 13 2018, 22:28:16)
               
              In : import regular
               
              In : regular
              Out: <module 'regular' (namespace)>
               
              In : import regular.a
               
              In : regular.a
              Out: <module 'regular.a' from '/Users/dongwm/mp/2020-01-02/regular/a/__init__.py'>
               
              In : regular.a.DATA
              Out: 'a'
               
              In : regular.b.DATA
              ---------------------------------------------------------------------------
              AttributeError                            Traceback (most recent call last)
              <ipython-input-8-2964870c96fb> in <module>
              ----> 1 regular.b.DATA
               
              AttributeError: module 'regular' has no attribute 'b'
               
              In : import regular.b
               
              In : regular.b.DATA
              Out: 'b'
              也就是說,在 Python 3 下即便沒有__init__.py也能正常 import 成功,不過模塊會顯示成<module 'XX' (namespace)>這樣,另外是對于其子包的使用不受影響。
               
              那么 Python 是怎么做到的呢?
               
              命名空間包 (Namespace package)
              這個特性是 Python 3.3 時引入的,PEP 鏈接: PEP420 。
               
              一個文件夾中沒有定義__init__.py也可以被導入的,只不過它不是以 Python 包的形式導入,而是以命名空間包 (Namespace package) 的形式被導入,所以顯示成上面看到的<module 'XX' (namespace)>這樣。
               
              不過,利用命名空間包的主要價值是能導入目錄分散的代碼。
               
              通過豆瓣的用法來理解
              豆瓣 開源了一些 Python 的項目,其中有一些內部版本還在廣泛的在各項目中使用,不過我們可以拿開源的來體驗一下問題,我們先安裝 2 個包吧:
               
              ❯ virtualenv venv --python=python2.7
              ❯ source venv/bin/activate
              ❯ git clone https://github.com/douban/douban-utils
              ❯ cd douban-utils/
              ❯ python setup.py install
              ❯ cd ../
              ❯ git clone https://github.com/douban/douban-sqlstore
              ❯ cd douban-sqlstore
              ❯ python setup.py install
              ❯ pip install mysqlclient  # douban-sqlstore依賴的MySQL-python已經不再維護,換一個
              ❯ cd ..
              現在看看怎么導入:
               
              ❯ pip install ipython==5.2  # IPython 6.X開始只支持Python 3了
              ❯ venv/bin/ipython
              In : from douban.sqlstore import SqlStore
               
              In : from douban.utils import ptrans
              這 2 個導入語句的代碼在不同的包中,但是 douban 是共用的空間。為什么用豆瓣這么個 namespace 呢?
               
              這個在延伸閱讀鏈接 2,也就是 Python Cookbook 里面被提到過。如果你所在公司或者團隊有大量的代碼,由不同的人來分散地維護,那么可以把其中不同的部分組織為文件目錄,但好的實踐是能用共同的包前綴將所有組件連接起來,不是將每一個部分作為獨立的包來安裝。
               
              這樣是不能用一開始提到的那個目錄名字為 regular 的常規包,需要使用命名空間包
               
              命名空間包的三種風格
              本文的重點啦:
               
              pkgutil 風格
              所謂風格其實就是用了那個 Python 模塊或者特性實現命名空間,pkgutil 風格就是在每個子包里面的__init__.py里面添加如下的代碼:
               
              ❯ cat pkgutil_style/a/__init__.py
              __path__ = __import__('pkgutil').extend_path(__path__, __name__)
              然后分別安裝并進入交互模式:
               
              ❯ python pkgutil_style/a/setup.py install
              ❯ python pkgutil_style/b/setup.py install
              setup.py 非常簡單,就是取了個不沖突的包名。然后體驗一下:
               
              ❯ venv/bin/ipython
              In : from pkgutil_style.a import DATA
               
              In : DATA
              Out: 'aa'
               
              In : from pkgutil_style.b import DATA
               
              In : DATA
              Out: 'bb'
              pkg_resources 風格
              它和 pkgutil 風格的區別就是子包里面的__init__.py里面添加的是如下代碼:
               
              __import__('pkg_resources').declare_namespace(__name__)
              效果和上面一樣。這種風格稱為 setuptools-style。
               
              上述 2 種風格在豆瓣項目中的已經體現了 (延伸閱讀鏈接 3):
               
              try:
                  __import__('pkg_resources').declare_namespace(__name__)
              except ImportError:
                  from pkgutil import extend_path
                  __path__ = extend_path(__path__, __name__)
              naive 風格 (Python3.3+)
              這是在 Python 3 時才可用的隱式的命名包的風格,也就是在命名空間下沒有__init__.py:
               
              ❯ tree naive_style -L 2
              naive_style  # 這里沒有⬅️
              ├── a
              │   ├── __init__.py
              │   └── setup.py
              └── b
                  ├── __init__.py
                  └── setup.py
              不過要注意,setup.py (除了明確使用 packages 列出包) 不能使用setuptools.find_packages(),而是要用setuptools.find_namespace_packages():
               
              ❯ cat naive_style/a/setup.py
              from setuptools import setup, find_namespace_packages
               
              setup(
                  name='pkg_3a',
                  version='1',
                  description='',
                  long_description='',
                  packages=find_namespace_packages(),
                  zip_safe=False,
              )
              怎么確認一個包是不是 naive 風格呢?如果__file__屬性為 None,那包是個命名空間:
               
              In : import naive_style
               
              In : import naive_style.a
               
              In : naive_style
              Out: <module 'naive_style' (namespace)>
               
              In : naive_style.__file__
               
              In : naive_style.a.__file__
              Out: '/Users/dongweiming/mp/2020-01-02/naive_style/a/__init__.py'
              PS: 注意這里和 Python Cookbook 里面說的不一樣.
               

              標簽: Python
              頂一下
              (0)
              0%
              踩一下
              (0)
              0%

              Google提供的廣告

              国产古代一级a毛片,国产成人午夜在线直播,write.as 当众老师
                <address id="tl19n"></address>
                  <address id="tl19n"><nobr id="tl19n"></nobr></address>

                    <address id="tl19n"></address>

                      <address id="tl19n"></address>
                      <form id="tl19n"></form>