1. 程式人生 > >Python筆記八(模塊和包)

Python筆記八(模塊和包)

初始化 python3.3 找到 glob OS sql 跨平臺 spec rsh

一、模塊

1、什麽是模塊

常見的場景:一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的後綴。

但其實import加載的模塊分為四個通用類別: 

  1 使用python編寫的代碼(.py文件)

  2 已被編譯為共享庫或DLL的C或C++擴展

  3 包好一組模塊的包

  4 使用C編寫並鏈接到python解釋器的內置模塊

2、為何要使用模塊?

如果你退出python解釋器然後重新進入,那麽你之前定義的函數或者變量都將丟失,因此我們通常將程序寫到文件中以便永久保存下來,需要時就通過python test.py方式去執行,此時test.py被稱為腳本script。

隨著程序的發展,功能越來越多,為了方便管理,我們通常將程序分成一個個的文件,這樣做程序的結構更清晰,方便管理。這時我們不僅僅可以把這些文件當做腳本去執行,還可以把他們當做模塊來導入到其他的模塊中,實現了功能的重復利用。

3、如何使用模塊

1)import

示例文件:自定義模塊my_module.py,文件名my_module.py,模塊名my_module

# my_module.py
print(from the my_module.py)
money = 1000

def read1():
    print(my_module->read1->money
, money) def read2(): print(my_module->read2 calling read1) read1() def change(): global money money = 0

part1:

模塊可以包含可執行的語句和函數的定義,這些語句的目的是初始化模塊,它們只在模塊名第一次遇到導入import語句時才執行(import語句是可以在程序中的任意位置使用的,且針對同一個模塊很import多次,為了防止你重復導入,python的優化手段是:第一次導入後就將模塊名加載到內存了,後續的import語句僅是對已經加載大內存中的模塊對象增加了一次引用,不會重新執行模塊內的語句),如下 :

# demo.py
import sys
import my_module  # 只在第一次導入時才執行my_module.py內代碼,此處的顯式效果是只打印一次‘from the my_module.py‘,當然其他的頂級代碼也都被執行了,只不過沒有顯示效果.
import my_module
import my_module
import my_module

print(sys.modules)
‘‘‘
執行結果:
from the my_module.py
{‘builtins‘: <module ‘builtins‘ (built-in)>, ‘sys‘: <module ‘sys‘ (built-in)>, ‘_frozen_importlib‘: <module ‘_frozen_importlib‘ (frozen)>, ‘_imp‘: <module ‘_imp‘ (built-in)>, ‘_warnings‘: <module ‘_warnings‘ (built-in)>, ‘_thread‘: <module ‘_thread‘ (built-in)>, ‘_weakref‘: <module ‘_weakref‘ (built-in)>, ‘_frozen_importlib_external‘: <module ‘_frozen_importlib_external‘ (frozen)>, ‘_io‘: <module ‘io‘ (built-in)>, ‘marshal‘: <module ‘marshal‘ (built-in)>, ‘nt‘: <module ‘nt‘ (built-in)>, ‘winreg‘: <module ‘winreg‘ (built-in)>, ‘zipimport‘: <module ‘zipimport‘ (built-in)>, ‘encodings‘: <module ‘encodings‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\encodings\\__init__.py‘>, ‘codecs‘: <module ‘codecs‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\codecs.py‘>, ‘_codecs‘: <module ‘_codecs‘ (built-in)>, ‘encodings.aliases‘: <module ‘encodings.aliases‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\encodings\\aliases.py‘>, ‘encodings.utf_8‘: <module ‘encodings.utf_8‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\encodings\\utf_8.py‘>, ‘_signal‘: <module ‘_signal‘ (built-in)>, ‘__main__‘: <module ‘__main__‘ from ‘C:/Users/28163/PycharmProjects/python21期/day8/111.py‘>, ‘encodings.latin_1‘: <module ‘encodings.latin_1‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\encodings\\latin_1.py‘>, ‘io‘: <module ‘io‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\io.py‘>, ‘abc‘: <module ‘abc‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\abc.py‘>, ‘_weakrefset‘: <module ‘_weakrefset‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\_weakrefset.py‘>, ‘site‘: <module ‘site‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\site.py‘>, ‘os‘: <module ‘os‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\os.py‘>, ‘errno‘: <module ‘errno‘ (built-in)>, ‘stat‘: <module ‘stat‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\stat.py‘>, ‘_stat‘: <module ‘_stat‘ (built-in)>, ‘ntpath‘: <module ‘ntpath‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\ntpath.py‘>, ‘genericpath‘: <module ‘genericpath‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\genericpath.py‘>, ‘os.path‘: <module ‘ntpath‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\ntpath.py‘>, ‘_collections_abc‘: <module ‘_collections_abc‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\_collections_abc.py‘>, ‘_sitebuiltins‘: <module ‘_sitebuiltins‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\_sitebuiltins.py‘>, ‘_bootlocale‘: <module ‘_bootlocale‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\_bootlocale.py‘>, ‘_locale‘: <module ‘_locale‘ (built-in)>, ‘encodings.gbk‘: <module ‘encodings.gbk‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\encodings\\gbk.py‘>, ‘_codecs_cn‘: <module ‘_codecs_cn‘ (built-in)>, ‘_multibytecodec‘: <module ‘_multibytecodec‘ (built-in)>, ‘sysconfig‘: <module ‘sysconfig‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\sysconfig.py‘>, ‘encodings.cp437‘: <module ‘encodings.cp437‘ from ‘C:\\Users\\28163\\AppData\\Local\\Programs\\Python\\Python36\\lib\\encodings\\cp437.py‘>, ‘sitecustomize‘: <module ‘sitecustomize‘ from ‘C:\\Program Files\\JetBrains\\PyCharm 2017.3.4\\helpers\\pycharm_matplotlib_backend\\sitecustomize.py‘>, ‘my_module‘: <module ‘my_module‘ from ‘C:\\Users\\28163\\PycharmProjects\\python21期\\day8\\my_module.py‘>}
‘‘‘

【註意】我們可以從sys.modules中找到當前已經加載的模塊,sys.modules是一個字典,內部包含模塊名與模塊對象的映射,該字典決定了導入模塊時是否需要重新導入。

part2:

每個模塊都是一個獨立的名稱空間,定義在這個模塊中的函數,把這個模塊的名稱空間當做全局名稱空間,這樣我們在編寫自己的模塊時,就不用擔心我們定義在自己模塊中全局變量會在被導入時,與使用者的全局變量沖突。

# 測試一:money與my_module.money不沖突
# demo.py
import my_module

money = 10
print(my_module.money)

‘‘‘
執行結果:
from the my_module.py
1000
‘‘‘

# 測試二:read1與my_module.read1不沖突
# demo.py
import my_module

def read1():
    print(========)


my_module.read1()

‘‘‘
執行結果:
from the my_module.py
my_module->read1->money 1000
‘‘‘

# 測試三:執行my_module.change()操作的全局變量money仍然是my_module中的
# demo.py
import my_module

money = 1
my_module.change()
print(money)

‘‘‘
執行結果:
from the my_module.py
1
‘‘‘

part3:

首次導入模塊my_module時會做三件事:

1.為源文件(my_module模塊)創建新的名稱空間,在my_module中定義的函數和方法若是使用到了global時訪問的就是這個名稱空間。

2.在新創建的命名空間中執行模塊中包含的代碼,見初始導入import my_module。

【註意】導入模塊時到底執行了什麽(事實上函數定義也是“被執行”的語句,模塊級別函數定義的執行將函數名放入模塊全局名稱空間表,用globals()可以查看)

3.創建名字my_module來引用該命名空間。

【註意】這個名字和變量名沒什麽區別,都是‘第一類的’,且使用my_module.名字的方式可以訪問my_module.py文件中定義的名字,my_module.名字與test.py中的名字來自兩個完全不同的地方。

part4:

為模塊起別名,語法如下:

import my_module as sm
print(sm.money)
‘‘‘
執行結果
from the my_module.py
1000
‘‘‘

示範用法一:

有兩中sql模塊mysql和oracle,根據用戶的輸入,選擇不同的sql功能。

# mysql.py
def sqlparse():
    print(from mysql sqlparse)

# oracle.py
def sqlparse():
    print(from oracle sqlparse)

# test.py
db_type = input(>>: )
if db_type == mysql:
    import mysql as db
elif db_type == oracle:
    import oracle as db

db.sqlparse()

示範用法二:

為已經導入的模塊起別名的方式對編寫可擴展的代碼很有用,假設有兩個模塊xmlreader.py和csvreader.py,它們都定義了函數read_data(filename):用來從文件中讀取一些數據,但采用不同的輸入格式。可以編寫代碼來選擇性地挑選讀取模塊。

if file_format == xml:
    import xmlreader as reader
elif file_format == csv:
    import csvreader as reader
data = reader.read_date(filename)

part5:

在一行導入多個模塊。

import sys, os, re

2)from ... import

part1:

對比import my_module,會將源文件的名稱空間‘my_module‘帶到當前名稱空間中,使用時必須是my_module.名字的方式。

而from 語句相當於import,也會創建新的名稱空間,但是將my_module中的名字直接導入到當前的名稱空間中,在當前名稱空間中,直接使用名字就可以了。

from my_module import read1,read2

這樣在當前位置直接使用read1和read2就好了,執行時,仍然以my_module.py文件全局名稱空間

#測試一:導入的函數read1,執行時仍然回到my_module.py中尋找全局變量money
#demo.py
from my_module import read1
money=100
read1()
‘‘‘
執行結果:
from the my_module.py
spam->read1->money 1000
‘‘‘

#測試二:導入的函數read2,執行時需要調用read1(),仍然回到my_module.py中找read1()
#demo.py
from my_module import read2
def read1():
    print(==========)
read2()

‘‘‘
執行結果:
from the my_module.py
my_module->read2 calling read1
my_module->read1->money 1000
‘‘‘

如果當前有重名read1或者read2,那麽會有覆蓋效果。

# 測試三:導入的函數read1,被當前位置定義的read1覆蓋掉了
# demo.py
from my_module import read1
def read1():
    print(==========)
read1()
‘‘‘
執行結果:
from the my_module.py
==========
‘‘‘

需要特別強調的一點是:python中的變量賦值不是一種存儲操作,而只是一種綁定關系,如下:

from my_module import money, read1
money = 100  # 將當前位置的名字money綁定到了100
print(money)  # 打印當前的名字
read1()  # 讀取my_module.py中的名字money,仍然為1000
‘‘‘
from the my_module.py
100
my_module->read1->money 1000
‘‘‘

part2:

支持as與導入多行

#as用法
from my_module import read1 as read
#導入多行
from my_module import (read1, 
                       read2, 
                       money)

part3:

from my_module import * 把my_module中所有的不是以下劃線(_)開頭的名字都導入到當前位置,大部分情況下我們的python程序不應該使用這種導入方式,因為*你不知道你導入什麽名字,很有可能會覆蓋掉你之前已經定義的名字。而且可讀性極其的差,在交互式環境中導入時沒有問題。

from my_module import * #將模塊my_module中所有的名字都導入到當前名稱空間
print(money)
print(read1)
print(read2)
print(change)

‘‘‘
執行結果:
from the my_module.py
1000
<function read1 at 0x1012e8158>
<function read2 at 0x1012e81e0>
<function change at 0x1012e8268>

在my_module.py中新增一行

__all__=[money,read1] #這樣在另外一個文件中用from my_module import *就這能導入列表中規定的兩個名字

【註意】如果my_module.py中的名字前加_,即_money,則from my_module import *,則_money不能被導入

【思考】假如有兩個模塊a,b。我可不可以在a模塊中import b ,再在b模塊中import a?(不能)

part4:模塊的加載與修改

考慮到性能的原因,每個模塊只被導入一次,放入字典sys.modules中,如果你改變了模塊的內容,你必須重啟程序,python不支持重新加載或卸載之前導入的模塊。

有的同學可能會想到直接從sys.modules中刪除一個模塊不就可以卸載了嗎,註意了,你刪了sys.modules中的模塊對象仍然可能被其他程序的組件所引用,因而不會被清除。

特別的對於我們引用了這個模塊中的一個類,用這個類產生了很多對象,因而這些對象都有關於這個模塊的引用。

如果只是你想交互測試的一個模塊,使用 importlib.reload(), e.g. import importlib; importlib.reload(modulename),這只能用於測試環境。

#aa.py
def func1():
    print(func1)

#test.py
import time, importlib
import aa

time.sleep(20)
importlib.reload(aa)
aa.func1()

在20秒的等待時間裏,修改aa.py中func1的內容,等待test.py的結果。然後打開importlib註釋,重新測試。

3)把模塊當做腳本執行

我們可以通過模塊的全局變量__name__來查看模塊名:

if __name__ == ‘__main__‘:

if __name__ == ‘模塊名‘:

作用:用來控制.py文件在不同的應用場景下執行不同的邏輯

通俗的理解:

__name__ == ‘__main__‘:假如你叫小明.py,在朋友眼中,你是小明(__name__ == ‘小明‘);在你自己眼中,你是你自己(__name__ == ‘__main__‘)

if __name__ == ‘__main__‘的意思是:當.py文件被直接運行時,if __name__ == ‘__main__‘之下的代碼塊將被運行;當.py文件以模塊形式被導入時,if __name__ == ‘__main__‘之下的代碼塊不被運行。

def fib(n):   
    a, b = 0, 1
    while b < n:
        print(b, end= )
        a, b = b, a+b
    print()

if __name__ == "__main__":
    print(__name__)
    num = input(num :)
    fib(int(num))

4)模塊搜索路徑

python解釋器在啟動時會自動加載一些模塊,可以使用sys.modules查看。

在第一次導入某個模塊時(比如my_module),會先檢查該模塊是否已經被加載到內存中(當前執行文件的名稱空間對應的內存),如果有則直接引用

如果沒有,解釋器則會查找同名的內建模塊,如果還沒有找到就從sys.path給出的目錄列表中依次尋找my_module.py文件。

所以總結模塊的查找順序是:內存中已經加載的模塊->內置模塊->sys.path路徑中包含的模塊

【註意】我們自定義的模塊名不應該與系統內置模塊重名。

在初始化後,python程序可以修改sys.path,路徑放到前面的優先於標準庫被加載。

1 >>> import sys
2 >>> sys.path.append(/a/b/c/d)
3 >>> sys.path.insert(0,/x/y/z) #排在前的目錄,優先被搜索

註意:搜索時按照sys.path中從左到右的順序查找,位於前的優先被查找,sys.path中還可能包含.zip歸檔文件和.egg文件,python會把.zip歸檔文件當成一個目錄去處理。

#首先制作歸檔文件:zip module.zip foo.py bar.py

import sys
sys.path.append(module.zip)
import foo,bar

#也可以使用zip中目錄結構的具體位置
sys.path.append(module.zip/lib/python)

#windows下的路徑不加r開頭,會語法錯誤
sys.path.insert(0,rC:\Users\Administrator\PycharmProjects\a)

至於.egg文件是由setuptools創建的包,這是按照第三方python庫和擴展時使用的一種常見格式,.egg文件實際上只是添加了額外元數據(如版本號,依賴項等)的.zip文件。

需要強調的一點是:只能從.zip文件中導入.py,.pyc等文件。使用C編寫的共享庫和擴展塊無法直接從.zip文件中加載(此時setuptools等打包系統有時能提供一種規避方法),且從.zip中加載文件不會創建.pyc或者.pyo文件,因此一定要事先創建他們,來避免加載模塊是性能下降。

技術分享圖片
#官網鏈接:https://docs.python.org/3/tutorial/modules.html#the-module-search-path
搜索路徑:
當一個命名為my_module的模塊被導入時
    解釋器首先會從內建模塊中尋找該名字
    找不到,則去sys.path中找該名字

sys.path從以下位置初始化
執行文件所在的當前目錄
PTYHONPATH(包含一系列目錄名,與shell變量PATH語法一樣)
依賴安裝時默認指定的

註意:在支持軟連接的文件系統中,執行腳本所在的目錄是在軟連接之後被計算的,換句話說,包含軟連接的目錄不會被添加到模塊的搜索路徑中

在初始化後,我們也可以在python程序中修改sys.path,執行文件所在的路徑默認是sys.path的第一個目錄,在所有標準庫路徑的前面。這意味著,當前目錄是優先於標準庫目錄的,需要強調的是:我們自定義的模塊名不要跟python標準庫的模塊名重復。
官網解釋

5)編譯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以前的版本應該是這樣)

2 如果源文件不存在,那麽緩存的結果也不會被使用,如果想在沒有源文件的情況下來使用編譯後的結果,則編譯後的結果必須在源目錄下

【註意】

1.模塊名區分大小寫,foo.py與FOO.py代表的是兩個模塊

2.你可以使用-O或者-OO轉換python命令來減少編譯模塊的大小

3.在速度上從.pyc文件中讀指令來執行不會比從.py文件中讀指令執行更快,只有在模塊被加載時,.pyc文件才是更快的

4.只有使用import語句是才將文件自動編譯為.pyc文件,在命令行或標準輸入中指定運行腳本則不會生成這類文件,因而我們可以使用compieall模塊為一個目錄中的所有模塊創建.pyc文件

6、dir()函數

內建函數dir是用來查找模塊中定義的名字,返回一個有序字符串列表

import my_module
print(dir(my_module))
‘‘‘
執行結果
from the my_module.py
[‘__builtins__‘, ‘__cached__‘, ‘__doc__‘, ‘__file__‘, ‘__loader__‘, ‘__name__‘, ‘__package__‘, ‘__spec__‘, ‘change‘, ‘money‘, ‘read1‘, ‘read2‘]
‘‘‘

【註意】

1 如果沒有參數,dir()列舉出當前定義的名字;

2 dir()不會列舉出內建函數或者變量的名字,它們都被定義到了標準模塊builtin中,可以列舉出它們。

二、包

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

1、import

2、from ... import

3、__init__.py文件

4、form glance.api import *

5、絕對導入和相對導入

6、單獨導入包

Python筆記八(模塊和包)