1. 程式人生 > >[Python]第十章 開箱即用

[Python]第十章 開箱即用

文章目錄


標準安裝包包含一組稱為標準庫的模組

10.1模組

>>> import math#匯入模組
>>> math.sin(0)#呼叫模組中的方法
0.0

10.1.1模組就是程式

在C:/Users/XXXX/Downloads/python目錄下有一個檔案hello.py,這個檔案中寫了一串可執行的程式碼,那麼這個檔案就可以作為模組匯入程式

# hello.py 
print("Hello, world!") 

首先需要將該目錄設定為系統路徑,這一步告訴直譯器,除了通常查詢的位置,還可以在該目錄下查詢,注意該種方式是隻是一次性的

>>>import sys
>>>sys.path.append('C:/Users/XXXX/Downloads/python')

然後匯入該模組

>>>import hello
Hello, world!

執行成功後,該目錄下回生成一個名為__pycache__的子目錄,可以刪除,必要時會重新建
再次匯入時將沒有任何動作,即使內容發生修改,因為模組不是用來執行操作的,而是用於定義變數、函式、類等,這些動作只需要做一次

>>> import hello
>>>

如果hello模組在執行時發生了修改,的確需要重新載入,可以使用importlib模組裡面的reload函式

>>> import importlib
>>> hello = importlib.reload(hello)
Hello, new world!

如果已經用之前模組中的類例項化了物件,重新載入模組後,該物件仍然是舊版模組類的物件

10.1.2模組是用來下定義的

讓模組值得被建立的原因在於他們像類一樣,有自己的作用域,這意味著在模組中定義的類和函式對其進行賦值的變數都將成為模組的屬性

1.在模組中定義函式

新建一個py檔案,寫一個函式

# hello2.py
def hello():
	print("Hello, world!")

匯入模組

>>> import hello2

如下訪問該函式:

>>> hello2.hello()
Hello, world!

這樣使用模組的意義是增加程式碼的重用性,將程式碼儲存為模組,在需要用的時候訪問它,而不需要重新編寫。

2.在模組程式碼中新增測試程式碼

新建一個py檔案,寫一個函式

# hello3.py
def hello():
	print("Hello, China!")
# 一個測試:
hello()

這塊程式碼直接作為普通程式可以執行,當做模組匯入另外一個程式中,hello()函式也能被執行

>>> import hello3
Hello, China!
>>> hello3.hello()
Hello, China!

使用變數__name__檢查模組作為程式執行還是被匯入另外一個程式

>>> __name__
'__main__'#當前執行的是主程式
>>> hello3.__name__
'hello3'#此時該變數__name__被賦值成該模組的名稱

將測試程式碼放入if語句

#hello4.py 
def hello(): 
 print("Hello, china") 
def test(): 
 hello() 
if __name__ == '__main__': test() #如果作為一個普通程式自己執行的時候會呼叫test(),當被當作模組匯入的時候不呼叫
>>> import hello4#不會自動執行測試程式碼
>>> hello4.hello()#直接訪問該函式
Hello,china
>>> hello4.test()#通過測試方法訪問該函式
Hello,china

10.1.3讓模組可用

之前需要import sys,sys.path.append(‘目錄’)才能讓直譯器找到模組的位置,如果一開始就讓sys.path包含正確的目錄,有兩種方式:

1.將模組放入正確的位置

只需找出原本直譯器去哪找

>>> import sys,pprint
>>> pprint.pprint(sys.path)
['',
 'G:\\Anaconda3\\python36.zip',
 'G:\\Anaconda3\\DLLs',
 'G:\\Anaconda3\\lib',
 'G:\\Anaconda3',
 'G:\\Anaconda3\\lib\\site-packages',
 'G:\\Anaconda3\\lib\\site-packages\\win32',
 'G:\\Anaconda3\\lib\\site-packages\\win32\\lib',
 'G:\\Anaconda3\\lib\\site-packages\\Pythonwin']

Notice:pprint是個卓越的列印函式,能夠更妥善地列印輸出。相比較print,可以只能換行,展現的格式跟佈局更合理
打印出來列表裡面的每個元素都是直譯器去查詢的目錄,將模組放置在其中任意一個目錄下即可。但目錄site-packages是最佳的選擇,因為它就是用來放置模組的。

2.告訴直譯器去哪裡找

對於以下情況,模組不便直接放在上述目錄下:
 不希望Python直譯器的目錄中充斥著你編寫的模組。
 沒有必要的許可權,無法將檔案儲存到Python直譯器的目錄中。
 想將模組放在其他地方。
那麼就需要告訴直譯器去實際存放模組的地方找
一種方法是之前介紹的sys,path.append()修改路徑,標準做法是將存放模組的目錄新增到環境變數PYTHONPATH中(計算機-屬性),命令export PYTHONPATH=$PYTHONPATH:~/python (base shell)
另外還可以使用路徑配置檔案.pth

10.1.4包

為組織模組,編組為包,包就是另一種模組,但他們可包含其他模組。模組是.py檔案,而包是一個目錄。要被Python視為包,目錄必須包含檔案__init__.py
例如,如果有一個名為constants的包,而檔案constants/init.py包含語句PI = 3.14,就可以像下面這樣做:

>>>import constants 
>>>print(constants.PI) 

要將模組加入包,只需將模組檔案放在包目錄中即可,也可以在包中巢狀其他包
以下語法都是合法的:

importimport.模組
fromimport 模組

10.2探索模組

10.2.1模組包含什麼

探索標準模組copy

1.使用函式dir
>>>import copy
>>>dir(copy)

如果只打印那些不含下劃線,可供外部使用的所有屬性,可使用一下列表推導篩出

>>> [n for n in dir(copy) if not n.startswith('_')] 
['Error', 'PyStringMap', 'copy', 'deepcopy', 'dispatch_table', 'error', 'name', 't', 'weakref'] 
2.變數__all__

在dir(copy)列印的完整列表中包含__all__,這個變數包含一個列表。

>>> copy.__all__
['Error', 'copy', 'deepcopy']

它是在模組copy中像下面這樣設定的(這些程式碼是直接從copy.py複製而來的):
__all__ = [“Error”, “copy”, “deepcopy”]
旨在定義模組共有的介面,它告訴直譯器從這個模組匯入的所有的名稱意味著什麼
因此,使用 from copy import *只能得到上述列出的3個函式
於是要匯入其他屬性,例如pystringMap,需要顯示匯入:import copy並使用copy.PyStringMap;或者使用from copy import pystringMap
編寫模組時,像這樣設定__all__也很有用。因為模組可能包含大量其他程式不需要的變數、
函式和類,比較周全的做法是將它們過濾掉。如果不設定__all__,則會在以import *方式匯入時,匯入所有不以下劃線打頭的全域性名稱。

10.2.2使用help獲取幫助

>>> help(copy.copy) #獲取有關函式copy
Help on function copy in module copy: 
copy(x) 
  	Shallow copy operation on arbitrary Python objec 
See the module's __doc__ string for more info. 

實際上,前面的幫助資訊是從函式copy的文件字串中提取的:

>>> print(copy.copy.__doc__) 
Shallow copy operation on arbitrary Python objects. 
 	See the module's __doc__ string for more info. 

10.2.3文件

當然可以直接訪問這個模組的doc文件

>>> print(copy.__doc__)
Generic (shallow and deep) copying operations.
Interface summary:
        import copy
        x = copy.copy(y)        # make a shallow
。。。。。。

Python庫參考手冊”(https://docs.python.org/library)

10.2.4使用原始碼

查詢原始碼,一種辦法是像直譯器那樣通過sys.path來查詢,但更快捷的方式是檢視模組的特性__file__

>>> print(copy.__file__)
G:\Anaconda3\lib\copy.py

從該路徑找到後,用編輯器開啟,注意不要儲存修改的內容

10.3標準庫:一些深受歡迎的模組

10.3.1 sys

訪問與python直譯器緊密相關的變數和函式

函式/變數 描 述
argv 命令列引數,包括指令碼名https://blog.csdn.net/sunny_580/article/details/78188716
exit([arg]) 退出當前程式,可通過可選引數指定返回值或錯誤訊息(finally子句依然會執行)
modules 一個字典,將模組名對映到載入的模組
path 一個列表,包含要在其中查詢模組的目錄的名稱
Platform 一個平臺識別符號,如sunos5或win32
stdin 標準輸入流——一個類似於檔案的物件
stdout 標準輸出流——一個類似於檔案的物件
stderr 標準錯誤流——一個類似於檔案的物件

簡單地說,Python從sys.stdin獲取輸入(例如,用於input中),並將輸出列印到sys.stdout。
CASE:反轉列印命令列引數

# reverseargs.py 
import sys 
args = sys.argv[1:] #this is a test
args.reverse() 
print(' '.join(args))
或者
print(' '.join(reversed(sys.argv[1:]))) 
>>> python reverseargs.py this is a test
test a is this 

這裡在shell或者其他直譯器輸入的python reverseargs.py後面的‘this is a test’就是sys.argv[1:] ,程式實現了將輸入的內容傳給reverseargs檔案

10.3.2 os

訪問多個作業系統服務

函式/變數 描 述
environ 包含環境變數的對映
system(command) 在子shell中執行作業系統命令
sep 路徑中使用的分隔符
pathsep 分隔不同路徑的分隔符
linesep 行分隔符(’\n’、’\r’或’\r\n’)
urandom(n) 返回n個位元組的強加密隨機資料

CASE:開啟瀏覽器

import os
#命令system可用於執行任何外部程式
#開啟記事本程式
os.system('notepad')
os.system(r'C:\"Program Files (x86)"\Google\Chrome\Application\chrome.exe')#有錯誤
os.startfile(r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe')

更佳的方案

import webbrowser
webbrowser.open('http://www.taobao.com')

Extend: 1.getcwd() #獲取當前路徑 ,chdir() #改變當前路徑

>>>import os
>>>localpath=os.getcwd()
>>>print(localpath)
-----------------------
C:\Users\xxxx\python_test

>>>os.chdir(r'C:\Users\xxxx\Downloads\python')
>>>print(os.getcwd())
--------------
C:\Users\xxxx\Downloads\python

Extend: 2.os.path.join(path, *paths) # Join two (or more) paths.

>>>newpath=os.path.join(localpath,'temp')
>>>print(newpath)
C:\Users\xxxx\python_test\temp

Extend: 3.sys.exit()和os.exit(),exit()/quit()

  1. sys.exit(n) 退出程式引發SystemExit異常, 可以捕獲異常執行些清理工作. n預設值為0, 表示正常退出. 其他都是非正常退出. 還可以sys.exit(“sorry, goodbye!”); 一般主程式中使用此退出.
  2. os._exit(n), 直接退出, 不拋異常, 不執行相關清理工作. 常用在子程序的退出.
  3. exit()/quit(), 跑出SystemExit異常. 一般在互動式shell中退出時使用.
    https://blog.csdn.net/index20001/article/details/74294945

10.3.3 fileinput

讀寫檔案

函 數 描 述
input([files[, inplace[, backup]]]) 幫助迭代多個輸入流中的行
filename() 返回當前檔案的名稱
lineno() 返回(累計的)當前行號
filelineno() 返回在當前檔案中的行號
isfirstline() 檢查當前行是否是檔案中的第一行
isstdin() 檢查最後一行是否來自sys.stdin
nextfile() 關閉當前檔案並移到下一個檔案
close() 關閉序列

CASE在Python指令碼中新增行號

# numberlines.py 
import fileinput 
for line in fileinput.input(inplace=True): #inplace:是否將標準輸出(print方法)的結果寫回檔案;如果不為TRUE,則文件內容不會改變,執行的結果將在控制檯打印出來
	line = line.rstrip() 
	num = fileinput.lineno() 
	print('{:<50} # {:2d}'.format(line, num)) #{:<50} 左對齊,寬度50, {:2d }表示兩個寬度的10進位制數顯示。

如果像下面這樣執行這個程式,並將其作為引數傳入:

>>> python numberlines.py text.txt

text.txt會作為引數傳入fileinput.input(inplace=True),實際上後面跟著多個檔案,如python numberlines.py text.txt temp.txt,會將後面所有檔案的行一一處理。
如果只輸入python numberlines.py或者python numberlines.py-,那麼就會預設sys.stdin等待輸入,對輸入的內容進行處理。

10.3.4集合、堆和雙端佇列

Python中有用的資料結構除了字典(散列表)和列表(動態陣列),還有一些又是也能排上用場

1.集合

由內建類Set實現

>>>set([0,1,2,3,4])
>>>set(range(5))
{0, 1, 2, 3, 4}

Notice:{}是一個空字典而非空集合,a=set()建立一個空集合
必須在不提供任何引數的情況下呼叫set。
集合主要用於成員資格檢查,因此將忽略重複的元素:

>>> {0, 1, 2, 3, 0, 1, 2, 3, 4, 5} 
{0, 1, 2, 3, 4, 5} 

與字典一樣,集合中元素的排列順序是不確定的,因此不能依賴於這一點。

>>> {'fee', 'fie', 'foe'} 
{'foe', 'fee', 'fie'} 

對集合進行計算
求並集 .union |

>>> a = {1, 2, 3}
>>> b = {2, 3, 4}
>>> a.union(b)
{1, 2, 3, 4}
>>> a | b
{1, 2, 3, 4}

取交集 .intersection &

>>> a.intersection(b)
{2, 3}
>>> a & b
{2, 3}
>>> c = a & b

a是否包含c .issubset <=

>>> c.issubset(a)
True
>>> c <= a
True

C是否包含a .issuperset >=

>>> c.issuperset(a)
False
>>> c >= a
False

從a中返回在b中不存在的元素 .difference -

>>> a.difference(b)
{1}
>>> a - b
{1}

從ab中返回在交集中不存在的元素 .symmetric_difference ^

>>> a.symmetric_difference(b)
{1, 4}
>>> a   b
{1, 4}

複製 .copy()

>>> a.copy()
{1, 2, 3}
>>> a.copy() is a
False

計算兩個集合的並集的函式時,set中方法union的未關聯版本

>>>my_sets = [{3,88,99}] 
>>>my_sets.append(set(range(0, 5)))
>>>my_sets
[{3, 88, 99}, {0, 1, 2, 3, 4}]
>>>import functools
>>>functools.reduce(set.union, my_sets) 
{0, 1, 2, 3, 4, 88, 99}

集合是可變的,因此不能用作字典中的鍵。集合只能包含不可變(可雜湊)的值,因此不能包含其他集合。但是有frozenset型別,它表示不可變(可雜湊)的集合。

>>>a={1,2,3,4}
>>>b={2,3,4,5}
>>> a.add(b)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: set objects are unhashable#集合不能包含其他集合
>>> a.add(frozenset(b)) 
>>>a
{1, 2, 3, 4, frozenset({2, 3, 4, 5})}

建構函式frozenset建立給定集合的副本。在需要將集合作為另一個集合的成員或字典中的鍵時,frozenset很有用。

2.堆 heapq

另一種著名的資料結構是堆(heap),它是一種優先佇列。優先佇列讓你能夠以任意順序新增物件,並隨時(可能是在兩次新增物件之間)找出(並刪除)最小的元素
這個模組名為heapq(其中的q表示佇列),它包含6個函式(如表10-5所示),其中前4個與堆操作直接相關。必須使用列表來表示堆物件本身。

函 數 描 述
heappush(heap, x) 將x壓入堆中
heappop(heap) 從堆中彈出最小的元素
heapify(heap) 讓列表具備堆特徵
heapreplace(heap, x) 彈出最小的元素,並將x壓入堆中
nlargest(n, iter) 返回iter中n個最大的元素
nsmallest(n, iter) 返回iter中n個最小的元素

heappush(heap, x)不能將它用於普通列表,而只能用於使用各種堆函式建立的列表。

from heapq import *
from random import shuffle
data=[8,5,6,7,1,3,4,2,0]
shuffle(data)#就地打亂序列seq
#data 直接輸出的序列將是無序的
heap=[]
for n in data:
    heappush(heap,n)
heap
[0, 1, 2, 4, 3, 8, 7, 6, 5]

它們雖然不是嚴格排序的,但必須保證一點:位置i處的元素總是大於位置i // 2處的元素(反過來說就是小於位置2 * i和2 * i + 1處的元素)。
這是底層堆演算法的基礎,稱為堆特徵(heap property)
函式heappop彈出最小的元素(總是位於索引0處),並確保剩餘元素中最小的那個位於索引0處(保持堆特徵)。

>>> heappop(heap) 
0 
>>> heappop(heap) 
1
>>> heappop(heap) 
2
>>> heap 
[3, 4, 6, 7, 5, 8] 

函式heapify通過執行儘可能少的移位操作將列表變成合法的堆(即具備堆特徵)。如果你的堆並不是使用heappush建立的,應在使用heappush和heappop之前使用這個函式。

>>> heap = [5, 8, 0, 3, 6, 7, 9, 1, 4, 2] 
>>> heapify(heap) 
>>> heap 
[0, 1, 5, 3, 2, 7, 9, 8, 4, 6] 

函式heapreplace從堆中彈出最小的元素,再壓入一個新元素。
相比於依次執行函式heappop和heappush,這個函式的效率更高

>>> heapreplace(heap, 0.5) 
0 #返回原最小值
>>> heap 
[0.5, 1, 5, 3, 2, 7, 9, 8, 4, 6] 
>>> heapreplace(heap, 10) 
0.5 
>>> heap 
[1, 2, 5, 3, 6, 7, 9, 8, 4, 10]
3.雙端佇列 collections

需要按新增元素的順序進行刪除時,雙端佇列很有用。 collections中,包含型別deque以及其他幾個集合(collection)型別。

>>> from collections import deque 
>>> q = deque(range(5)) 
>>> q.append(5) 
>>> q.appendleft(