天天看點

鐵樂學python_day29_子產品與包學習4

大部份内容摘自授課老師的部落格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 *

附:軟體規範開發參考目錄圖

鐵樂學python_day29_子產品與包學習4

end

2018-4-27