1. 程式人生 > >python學習日記(包——package)

python學習日記(包——package)

簡述——包

包是一種通過使用‘.模組名’來組織python模組名稱空間的方式。

注意:

1. 無論是import形式還是from...import形式,凡是在匯入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關於包才有的匯入語法

2. 包是目錄級的(資料夾級),資料夾是用來組成py檔案(包的本質就是一個包含__init__.py檔案的目錄)

3. import匯入檔案時,產生名稱空間中的名字來源於檔案,import 包,產生的名稱空間的名字同樣來源於檔案,即包下的__init__.py,匯入包本質就是在匯入該檔案

強調:

  1. 在python3中,即使包下沒有__init__.py檔案,import 包仍然不會報錯,而在python2中,包下一定要有該檔案,否則import 包報錯

  2. 建立包的目的不是為了執行,而是被匯入使用,記住,包只是模組的一種形式而已,包即模組

包A和包B下有同名模組也不會衝突,如A.a與B.a來自兩個名稱空間

import os
os.makedirs('glance/api')
os.makedirs('glance/cmd')
os.makedirs('glance/db')
l = []#注意列表的用法與作用
l.append(open('glance/__init__.py','w'))
l.append(open('glance/api/__init__.py','w'))
l.append(open(
'glance/api/policy.py','w')) l.append(open('glance/api/versions.py','w')) l.append(open('glance/cmd/__init__.py','w')) l.append(open('glance/cmd/manage.py','w')) l.append(open('glance/db/models.py','w')) l.append(open('glance/db/__init__.py','w')) map(lambda f:f.close() ,l)#同時關閉多個檔案
建立目錄層次程式碼
glance/                   #
Top-level package ├── __init__.py #Initialize the glance package ├── api #Subpackage for api │ ├── __init__.py │ ├── policy.py │ └── versions.py ├── cmd #Subpackage for cmd │ ├── __init__.py │ └── manage.py └── db #Subpackage for db ├── __init__.py └── models.py
目錄的結構
#檔案內容

#policy.py
def get():
    print('from policy.py')

#versions.py
def create_resource(conf):
    print('from version.py: ',conf)

#manage.py
def main():
    print('from manage.py')

#models.py
def register_models(engine):
    print('from models.py: ',engine)
檔案內容

注意事項

1.關於包相關的匯入語句也分為import和from ... import ...兩種,但是無論哪種,無論在什麼位置,在匯入時都必須遵循一個原則:凡是在匯入時帶點的,點的左邊都必須是一個包,否則非法。可以帶有一連串的點,如item.subitem.subsubitem,但都必須遵循這個原則。

2.對於匯入後,在使用時就沒有這種限制了,點的左邊可以是包,模組,函式,類(它們都可以用點的方式呼叫自己的屬性)。

3.對比import item 和from item import name的應用場景:
如果我們想直接使用name那必須使用後者。

import

在與包glance同級別的檔案中測試

import glance.api.policy
glance.api.policy.get()

from...import...

需要注意的是from後import匯入的模組,必須是明確的一個不能帶點,否則會有語法錯誤,如:from a import b.c是錯誤語法

我們在與包glance同級別的檔案中測試 

from glance.db import models
models.register_models('abc')

from glance.cmd.manage import main
main()

__init__.py檔案

不管是哪種方式,只要是第一次匯入包或者是包的任何其他部分,都會依次執行包下的__init__.py檔案(我們可以在每個包的檔案內都列印一行內容來驗證一下),這個檔案可以為空,但是也可以存放一些初始化包的程式碼。

with open('glance\__init__.py',encoding='utf-8') as f:
    print(f.readline())
    
import glance

from glance.api import *

在講模組時,我們已經討論過了從一個模組內匯入所有*,此處我們研究從一個包匯入所有*。

此處是想從包api中匯入所有,實際上該語句只會匯入包api下__init__.py檔案中定義的名字,我們可以在這個檔案中定義__all___:

#在api\__init__.py裡
x = 100
def f():
    print('from api.__init__.py',x)
__all__ = ['x','policy','f']

此時我們在與glance同級的檔案中執行from glance.api import *就匯入__all__中的內容(versions仍然不能匯入)。

from glance.api import *
policy.get()
f()

追加(注意)

如果想要這樣呼叫(glance同級檔案下)

import glance
glance.api.policy.get()

需要在__init__檔案裡面新增一些初始程式,如下:

#在glance\__init__.py
from glance import api

#在api\__init__.py
from glance.api import policy

絕對匯入(sys.path)和相對匯入

概述

ps:在上面追加的基礎上↑

我們的最頂級包glance是寫給別人用的,然後在glance包內部也會有彼此之間互相匯入的需求,這時候就有絕對匯入和相對匯入兩種方式:

絕對匯入:以glance作為起始

相對匯入:用.或者..的方式最為起始(只能在一個包中使用,不能用於不同目錄內)

例如:我們在glance/api/version.py中想要匯入glance/cmd/manage.py

在glance/api/version.py

#絕對匯入
from glance.cmd import manage
manage.main()

#相對匯入
from ..cmd import manage
manage.main()

測試結果:注意一定要在於glance同級的檔案中測試

from glance.api import versions

注意!!

特別需要注意的是:可以用import匯入內建或者第三方模組(已經在sys.path中),但是要絕對避免使用import來匯入自定義包的子模組(沒有在sys.path中),應該使用from... import ...的絕對或者相對匯入,且包的相對匯入只能用from的形式。

比如我們想在glance/api/versions.py中匯入glance/api/policy.py,有的人一看這兩個模組是在同一個目錄下,他是這樣做的:

 #在version.py中

import policy
policy.get()

沒錯,我們單獨執行version.py是一點問題沒有的,執行version.py的路徑搜尋就是從當前路徑開始的,於是在匯入policy時能在當前目錄下找到。

但是同時,子包中的模組version.py極有可能是被一個glance包同一級別的其他檔案匯入,比如我們在於glance同級下的一個test.py檔案中匯入version.py,如下:

from glance.api import versions

'''
執行結果:
ImportError: No module named 'policy'
'''

'''
分析:
此時我們匯入versions在versions.py中執行
import policy需要找從sys.path也就是從當前目錄找policy.py,
這必然是找不到的
'''

————————分割線————————

glance/                   

├── __init__.py      from glance import api
                             from glance import cmd
                             from glance import db

├── api                  

│   ├── __init__.py  from glance.api import policy
                              from glance.api import versions

│   ├── policy.py

│   └── versions.py

├── cmd                 from glance.cmd import manage

│   ├── __init__.py

│   └── manage.py

└── db                   from glance.db import models

    ├── __init__.py

    └── models.py
絕對匯入
glance/                   

├── __init__.py      from . import api  #.表示當前目錄
                     from . import cmd
                     from . import db

├── api                  

│   ├── __init__.py  from . import policy
                     from . import versions

│   ├── policy.py

│   └── versions.py

├── cmd              from . import manage

│   ├── __init__.py

│   └── manage.py    from ..api import policy   
                     #..表示上一級目錄,想再manage中使用policy中的方法就需要回到上一級glance目錄往下找api包,從api匯入policy

└── db               from . import models

    ├── __init__.py

    └── models.py
相對匯入

單獨匯入包

單獨匯入的結果

單獨匯入包名稱時不會匯入包中所有包含的所有子模組,如

import glance
glance.api.policy.get()

解決方法:

#在glance\__init__.py
from . import api

#在api\__init__.py
from . import policy

重新執行程式碼就OK了。

import glance之後直接呼叫模組中的方法

glance/                   

├── __init__.py     from .api import *
                    from .cmd import *
                    from .db import *    
├── api                  

│   ├── __init__.py   __all__ = ['policy','versions'] 

│   ├── policy.py

│   └── versions.py

├── cmd               __all__ = ['manage']    

│   ├── __init__.py

│   └── manage.py    

└── db                __all__ = ['models']              

    ├── __init__.py

    └── models.py


import glance
policy.get()#好像不行?
import glance
glance.policy.get()#
View Code

pass