大部份内容摘自授課老師的部落格http://www.cnblogs.com/Eva-J/
編譯python檔案
編譯python檔案是為了提高加載子產品的速度,強調強調強調:提高的是加載速度而絕非運作速度。
python解釋器會在__pycache__目錄下緩存每個子產品編譯後的版本,格式為:module.version.pyc。
通常會包含python的版本号。
例如,在CPython3版本下,my_module.py子產品會被緩存成__pycache__/my_module.cpython-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形式,
凡是在導入語句中(而不是在使用時)遇到帶點的,都要第一時間提高警覺:這是關于包才有的導入文法。
包是目錄級的(檔案夾級),檔案夾是用來組成py檔案(包的本質就是一個包含__init__.py檔案的目錄)。
import導入檔案時,産生名稱空間中的名字來源于檔案,import 包,
産生的名稱空間的名字同樣來源于檔案,即包下的__init__.py,導入包本質就是在導入該檔案。
強調:
在python3中,即使包下沒有__init__.py檔案,import 包仍然不會報錯,
而在python2中,包下一定要有該檔案,否則import 包報錯。
建立包的目的不是為了運作,而是被導入使用,記住,包隻是子產品的一種形式而已,包即子產品。
包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=
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