1. 程式人生 > >學習python的日常6

學習python的日常6

local 系列 規範 os模塊 傳遞 日誌文件 except 不存在 情況

錯誤、調試和測試:

錯誤處理:

try:
    print(‘try...‘)
    r = 10 / 0
    print(‘result:‘, r)
except ZeroDivisionError as e:
    print(‘except:‘, e)
finally:
    print(‘finally...‘)
print(‘END‘)

 首先是執行語句,然後發現錯誤了就會跳轉到執行except,語句,然後按順序執行,如果是正確的就不會執行except語句。

其中的Error還可以細分,錯誤本身也是一個類,都繼承自BaseException,所以盡量不要出現錯誤的父類和子類同時捕獲,

因為這時候只會執行父類的捕獲錯誤。

調用棧:

如果一個錯誤沒有被捕獲,就會一直往上拋,最後被python解釋器捕獲,出錯的時候通過分析錯誤的調用棧信息,可以定位

錯誤的位置。

記錄錯誤:

python內置的logging模塊可以非常容易地記錄錯誤信息

# err_logging.py

import logging

def foo(s):
    return 10 / int(s)

def bar(s):
    return foo(s) * 2

def main():
    try:
        bar(‘0‘)
    except Exception as e:
        logging.exception(e)

main()
print(‘END‘)

$ python3 err_logging.py
ERROR:root:division by zero
Traceback (most recent call last):
  File "err_logging.py", line 13, in main
    bar(‘0‘)
  File "err_logging.py", line 9, in bar
    return foo(s) * 2
  File "err_logging.py", line 6, in foo
    return 10 / int(s)
ZeroDivisionError: division by zero
END

拋出錯誤:

因為錯誤是class,然後捕獲一個錯誤就是捕獲到一個實例,如果要拋出錯誤,可以根據需要定義一個錯誤的class,

選擇好繼承關系,然後用raise語句拋出一個錯誤的實例。只有在必要的時候才定義我們自己的錯誤類型,如果可以,

盡量使用python內置的錯誤類型。

最後還有一種錯誤處理的方式:

# err_reraise.py

def foo(s): 
    n = int(s)
    if n==0:
        raise ValueError(‘invalid value: %s‘ % s)
    return 10 / n

def bar():
    try:
        foo(‘0‘)
    except ValueError as e:
        print(‘ValueError!‘)
        raise

bar()

 捕獲異常後,又把錯誤用過raise語句拋出去,是因為,捕獲錯誤的目的只是記錄一下,由於當前函數不知道

應該怎麽處理該錯誤,所以往上拋是一種很好的方式,最終會讓頂層調用者去處理。

調試:

1.一種方法簡單粗暴,直接用print()打印。

2.斷言,凡是用print()來輔助查看的都可以用斷言(assert)來替代。

3.logging,把print()替換為logging是第三種方式,logging可以指定記錄信息的級別,有debug,info,warning,error

等幾個級別,指定高級別的時候,低級別就不起作用了。

4.pdb,啟用python的調試器pdb,讓程序以單步方式運行,可以隨時查看運行狀態。pdb.set_trace(),也是用pdb,只

需要導入pdb,然後在可能出錯的地方放一個pdb.set_trace(),就可以設置一個斷點,到了之後就會暫停並進入pdb

調試環境,用p可以查看變量,用c繼續運行。

5.IDE,支持調試功能的IDE,有一些比較好的Python IDE。

單元測試:

用來對一個模塊、一個函數或者一個類來進行正確性檢驗的測試工作。我們給出一系列數據,然後期望輸出的結果是我們

預期的結果,如果不相符就說明我們的代碼中存在問題。

文檔測試:

利用註釋來告知代碼我們希望得到的結果,然後又一些工具來自動生成文檔。

IO編程:

IO指的是Input/Output,也就是輸入和輸出。IO編程中由於內外設備的速度不匹配,又可以分為同步IO和異步IO兩種方式,

現在涉及到的是同步IO

文件讀寫:

讀文件

>>> f = open(‘/Users/michael/test.txt‘, ‘r‘)

 如果不存在就會報錯,然後如果成功打開調用read()方法就可以一次性讀到內存當中,用一個str對象表示,要記得調用

close()方法來關閉文件,如果出錯是無法關閉文件的,f.close()也不會調用,所以用try...finally來實現,但是總寫會很麻煩,

所以可以調用python的with語句來自動調用close()方法:

with open(‘/path/to/file‘, ‘r‘) as f:
    print(f.read())

  想open()函數這種返回有個read()方法的對象,在python中統稱為file-like Object,除了file外還有很多其他的流,不過只要

有個read()方法就可以了。讀

>>> from io import StringIO
>>> f = StringIO()
>>> f.write(‘hello‘)
5
>>> f.write(‘ ‘)
1
>>> f.write(‘world!‘)
6
>>> print(f.getvalue())
hello world!

  操作二進制數據就需要使用BytesIO。

StringIO和BytesIO是在內存中操作str和bytes的方法,使得和讀寫文件具有一致的接口。

取二進制文件,用’rb‘模式打開文件就行;讀取字符編碼文本文件,添加一個encoding=’gkb‘,遇到

不規範的可以添加參數errors=‘ignore‘。

寫文件

>>> f = open(‘/Users/michael/test.txt‘, ‘w‘)
>>> f.write(‘Hello, world!‘)
>>> f.close()

 寫文件和讀文件一樣,不過區別在於傳入的標識符,‘w‘或者’wb‘表示文本文件或者寫二進制文件,如果要寫入特定編碼的文本

文件,要給open()傳入encoding參數,將字符串自動轉換成特定編碼。

StringIO和BytesIO

操作文件和目錄:

python的os模塊封裝了操作系統的目錄和文件操作,這些函數有的在os模塊中,有的在os.path模塊中。

序列化:

把變量從內存中變成可存儲或可傳輸的過程稱之為序列化,反過來把變量內容從序列化的對象重新讀到內存裏稱之為反序列化。

python提供pickle模塊來實現序列化,但是要把序列變得更通用、更符合Web標準,就可以使用json模塊。

json模塊的dumps()和loads()函數是用來序列化和反序列化的,如果默認的序列化和反序列化機制不滿足要求時,可以傳入更多

的參數來定制序列化或反序列化規則。

進程和線程:

多任務的實現方式有3種:1.多進程模式;

2.多線程模式;

3.多進程+多線程模式

線程時最小的執行單元,而進程由至少一個線程組成。

多進程:

在Unix/Linux下,可以使用fork()調用實現多進程。

如果要實現跨平臺的多進程,可以使用multiprocessing模塊。

subprocess模塊可以很方便地啟動一個子進程,然後控制其輸入和輸出。

進程間通信時通過Queue、Pipes等實現地。

多線程:

同一個進程內多個線程,python地標準庫提供了_thread和threading兩個模塊,絕大多數下使用threading這個高級模塊。

啟動一個線程就是把一個函數傳入並創建Thread實例,然後調用start()開始執行。多線程中,由於線程地調度是由系統

決定地,當線程交替執行時,很容易造成內容被亂改。為了確保變量,可以給線程上鎖,一個進程提供一個鎖,然後讓

線程去獲取該鎖,這樣線程就只有在獲得鎖的情況下才能執行語句,就不會造成沖突了。然後壞處的話只要時降低了

效率以及容易造成死鎖。Python的解釋器執行代碼時,有一個GIL鎖:Global Interpreter Lock,任何Python線程執行前,

必須先獲得GIL鎖。這個GIL全局鎖實際上把所有線程的執行代碼都給上鎖。

ThreadLocal:

import threading

# 創建全局ThreadLocal對象:
local_school = threading.local()

def process_student():
    # 獲取當前線程關聯的student:
    std = local_school.student
    print(‘Hello, %s (in %s)‘ % (std, threading.current_thread().name))

def process_thread(name):
    # 綁定ThreadLocal的student:
    local_school.student = name
    process_student()

t1 = threading.Thread(target= process_thread, args=(‘Alice‘,), name=‘Thread-A‘)
t2 = threading.Thread(target= process_thread, args=(‘Bob‘,), name=‘Thread-B‘)
t1.start()
t2.start()
t1.join()
t2.join()

  

局部變量在函數調用時很麻煩,一層一層傳遞很麻煩,全局變量local_school就是一個ThreadLocal對象,每個Thread

對它都可以讀寫student屬性,但互不影響。可以把local_school看成全局變量,每個屬性如local_school.student都是線

程的局部變量,可以任意讀寫而互不幹擾,也不用管理鎖的問題,ThreadLocal內部會處理。

ThreadLocal最常用的地方就是為每個線程綁定一個數據庫連接,HTTP請求,用戶身份信息等,這樣一個線程的所有調

用到的處理函數都可以非常方便地訪問這些資源。一個ThreadLocal變量雖然是全局變量,但每個線程都只能讀寫自己

線程的獨立副本,互不幹擾。ThreadLocal解決了參數在一個線程中各個函數都可以非常方便地訪問這些資源。

分布式進程:

Python地分布式進程接口簡單,封裝良好,適合需要把繁重任務分布到多臺機器地環境下。

註意Queue地作用是用來傳遞任務和接收結果,每個任務的描述數據量要盡量小。比如發送一個處理日誌文件的任務,

就不要發送幾百兆的日誌文件本身,而是發送日誌文件存放的完整路徑,由Worker進程再去共享的磁盤上讀取文件。

學習python的日常6