包是一種通過使用‘.模塊名’來組織python模塊名稱空間的方式。 1. 無論是import形式還是from...import形式, 凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關於包才有的導入語法。 2. 包是目錄級的(文件夾級),文件夾是用來組成py文件(包的本質就是... ...
大部份內容摘自授課老師的博客http://www.cnblogs.com/Eva-J/
編譯python文件
編譯python文件是為了提高載入模塊的速度,強調強調強調:提高的是載入速度而絕非運行速度。
python解釋器會在__pycache__目錄下緩存每個模塊編譯後的版本,格式為:module.version.pyc。
通常會包含python的版本號。
例如,在CPython3.3版本下,my_module.py模塊會被緩存成__pycache__/my_module.cpython-33.pyc。
這種命名規範保證了編譯後的結果多版本共存。
Python檢查源文件的修改時間與編譯的版本進行對比,如果過期就需要重新編譯。
這是完全自動的過程。並且編譯的模塊是平臺獨立的,
所以相同的庫可以在不同的架構的系統之間共用,即pyc是一種跨平臺的位元組碼,類似於JAVA和.NET,
是由python虛擬機來執行的,但是pyc的內容跟python的版本相關,不同的版本編譯後的pyc文件不同,
2.5編譯的pyc文件不能到3.5上執行,並且pyc文件是可以反編譯的,因而它的出現僅僅是用來提升模塊的載入速度的。
python解釋器在以下兩種情況下不檢測緩存。
1 如果是在命令行中被直接導入模塊,則按照這種方式,每次導入都會重新編譯,
並且不會存儲編譯後的結果(python3.3以前的版本應該是這樣)
python -m my_module.py
2 如果源文件不存在,那麼緩存的結果也不會被使用,
如果想在沒有源文件的情況下來使用編譯後的結果,則編譯後的結果必須在源目錄下。
提示:
1.模塊名區分大小寫,foo.py與FOO.py代表的是兩個模塊;
2.你可以使用-O或者-OO轉換python命令來減少編譯模塊的大小。
-O轉換會幫你去掉assert語句
-OO轉換會幫你去掉assert語句和__doc__文檔字元串
由於一些程式可能依賴於assert語句或文檔字元串,你應該在在確認需要的情況下使用這些選項。
3.在速度上從.pyc文件中讀指令來執行不會比從.py文件中讀指令執行更快,
只有在模塊被載入時,.pyc文件才是更快的。
4.只有使用import語句是才將文件自動編譯為.pyc文件,
在命令行或標準輸入中指定運行腳本則不會生成這類文件,
因而我們可以使用compieall模塊為一個目錄中的所有模塊創建.pyc文件。
模塊可以作為一個腳本(使用python -m compileall)編譯Python源
python -m compileall /module_directory 遞歸 編譯
如果使用python -O -m compileall /module_directory -l則只一層
命令行里使用compile()函數時,自動使用python -O -m compileall
詳見:https://docs.python.org/3/library/compileall.html#module-compileall
補充:dir()函數
內建函數dir是用來查找模塊中定義的名字,返回一個有序字元串列表:
import my_module
dir(my_module)
如果沒有參數,dir()列舉出當前定義的名字。
dir()不會列舉出內建函數或者變數的名字,它們都被定義到了標準模塊builtin中,可以列舉出它們,
import builtins
dir(builtins)
包
包是一種通過使用‘.模塊名’來組織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'))
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.db.models
glance.db.models.register_models('mysql')
from ... import ...
需要註意的是from後import導入的模塊,
必須是明確的一個不能帶點,否則會有語法錯誤,如:from a import b.c是錯誤語法
我們在與包glance同級別的文件中測試
from glance.db import models
models.register_models('mysql')
from glance.db.models import register_models
register_models('mysql')
__init__.py文件
不管是哪種方式,只要是第一次導入包或者是包的任何其他部分,
都會依次執行包下的__init__.py文件(我們可以在每個包的文件內都列印一行內容來驗證一下),
這個文件可以為空,但是也可以存放一些初始化包的代碼。
from glance.api import *
在講模塊時,我們已經討論過了從一個模塊內導入所有*,此處我們研究從一個包導入所有*。
此處是想從包api中導入所有,實際上該語句只會導入包api下__init__.py文件中定義的名字,
我們可以在這個文件中定義__all___:
在__init__.py中定義
x=10
def func():
print('from api.__init.py')
__all__=['x','func','policy']
此時我們在於glance同級的文件中執行from glance.api import *就導入__all__中的內容(versions仍然不能導入)。
from glance.api import *
glance/
├── __init__.py
├── api
│ ├── __init__.py __all__ = ['policy','versions']
│ ├── policy.py
│ └── versions.py
├── cmd __all__ = ['manage']
│ ├── __init__.py
│ └── manage.py
└── db __all__ = ['models']
├── __init__.py
└── models.py
from glance.api import *
policy.get()
絕對導入和相對導入
我們的最頂級包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
註意:在使用pycharm時,有的情況會為你多做一些事情,這是軟體相關的東西,會影響你對模塊導入的理解,
因而在測試時,一定要回到命令行去執行,模擬我們生產環境,你總不能拿著pycharm去上線代碼吧!!!
特別需要註意的是:
可以用import導入內置或者第三方模塊(已經在sys.path中),但是要絕對避免使用import來導入自定義包的子模塊(沒有在sys.path中),應該使用from... import ...的絕對或者相對導入,且包的相對導入只能用from的形式。
單獨導入包
單獨導入包名稱時不會導入包中所有包含的所有子模塊,
解決方法是子模塊中使用相對路徑引入上級(..上一級目錄)模塊或同級(.當前目錄)模塊
千萬別問:__all__不能解決嗎,__all__是用於控制from...import *
附:軟體規範開發參考目錄圖
end
2018-4-27