廖雪峰python摘錄二輪2
1 >>> def set_age(self, age): # 定義一個函數作為實例方法 2 ... self.age = age 3 ... 4 >>> from types import MethodType 5 >>> s.set_age = MethodType(set_age, s) # 給實例綁定一個方法 6 >>> s.set_age(25) # 調用實例方法 7 >>> s.age # 測試結果 8 25
2、四點需要註意: 1、類和實例都可以動態綁定屬性和方法。類綁定的屬性和方法可以作用於 實例,反之則不可以; 2、類動態綁定方法和實例綁定不同,綁定屬性時兩者相同 類綁定屬性:Student.sex = ‘M‘ 實例綁定屬性:s.sex = ‘M‘ 類綁定方法:from types import MethodType def set_sex(self, sex): self.sex = sex Student.set_sex = set_sex 實例綁定方法:from types import MethodType def set_sex(self, sex): self.sex = sex s.set_sex = MethodType(set_sex, s) 3、slots
3、動態語言和靜態語言最大的不同,就是函數和類的定義,不是編譯時定義的,而是運行時動態創建的。
4、
有的錯誤是程序編寫有問題造成的,比如本來應該輸出整數結果輸出了字符串,這種錯誤我們通常稱之為bug,bug是必須修復的。
有的錯誤是用戶輸入造成的,比如讓用戶輸入email地址,結果得到一個空字符串,這種錯誤可以通過檢查用戶輸入來做相應的處理。
還有一類錯誤是完全無法在程序運行過程中預測的,比如寫入文件的時候,磁盤滿了,寫不進去了,或者從網絡抓取數據,網絡突然斷掉了。這類錯誤也稱為異常,在程序中通常是必須處理的,否則,程序會因為各種問題終止並退出。
5、
1 try: 2 foo() 3 except ValueError as e: 4 print(‘ValueError‘) 5 except UnicodeError as e: 6 print(‘UnicodeError‘)
第二個except
永遠也捕獲不到UnicodeError
,因為UnicodeError
是ValueError
的子類,如果有,也被第一個except
給捕獲了。
6、使用try...except
捕獲錯誤還有一個巨大的好處,就是可以跨越多層調用,比如函數main()
調用foo()
,foo()
調用bar()
,結果bar()
出錯了,這時,只要main()
1 def foo(s): 2 return 10 / int(s) 3 4 def bar(s): 5 return foo(s) * 2 6 7 def main(): 8 try: 9 bar(‘0‘) 10 except Exception as e: 11 print(‘Error:‘, e) 12 finally: 13 print(‘finally...‘)
也就是說,不需要在每個可能出錯的地方去捕獲錯誤,只要在合適的層次去捕獲錯誤就可以了。這樣一來,就大大減少了寫try...except...finally
的麻煩。
7、
當程序出現錯誤,python會自動引發異常,也可以通過raise顯示地引發異常。一旦執行了raise語句,raise後面的語句將不能執行。這就是logging
的好處,它允許你指定記錄信息的級別,有debug
,info
,warning
,error
等幾個級別,當我們指定level=INFO
時,logging.debug
就不起作用了。同理,指定level=WARNING
後,debug
和info
就不起作用了。這樣一來,你可以放心地輸出不同級別的信息,也不用刪除,最後統一控制輸出哪個級別的信息。
logging
的另一個好處是通過簡單的配置,一條語句可以同時輸出到不同的地方,比如console和文件。
8、讀寫文件前,我們先必須了解一下,在磁盤上讀寫文件的功能都是由操作系統提供的,現代操作系統不允許普通的程序直接操作磁盤,所以,讀寫文件就是請求操作系統打開一個文件對象(通常稱為文件描述符),然後,通過操作系統提供的接口從這個文件對象中讀取數據(讀文件),或者把數據寫入這個文件對象(寫文件)。
很多時候,數據讀寫不一定是文件,也可以在內存中讀寫。
StringIO顧名思義就是在內存中讀寫str。
要把str寫入StringIO,我們需要先創建一個StringIO,然後,像文件一樣寫入即可:
1 >>> from io import StringIO 2 >>> f = StringIO() 3 >>> f.write(‘hello‘) 4 5 5 >>> f.write(‘ ‘) 6 1 7 >>> f.write(‘world!‘) 8 6 9 >>> print(f.getvalue()) 10 hello world!
TypeError: initial_value must be unicode or None, not str 錯誤處理》》》
response.read()
returns an instance of bytes
while StringIO
is an in-memory stream for text only. Use BytesIO
instead.
From What‘s new in Python 3.0 - Text Vs. Data Instead Of Unicode Vs. 8-bit
9、
BytesIO
StringIO操作的只能是str,如果要操作二進制數據,就需要使用BytesIO。
BytesIO實現了在內存中讀寫bytes,我們創建一個BytesIO,然後寫入一些bytes:
10、
Python語言特定的序列化模塊是pickle
,但如果要把序列化搞得更通用、更符合Web標準,就可以使用json
模塊。
json
模塊的dumps()
和loads()
函數是定義得非常好的接口的典範。當我們使用時,只需要傳入一個必須的參數。但是,當默認的序列化或反序列機制不滿足我們的要求時,我們又可以傳入更多的參數來定制序列化或反序列化的規則,既做到了接口簡單易用,又做到了充分的擴展性和靈活性。
11、
由於Python是跨平臺的,自然也應該提供一個跨平臺的多進程支持。multiprocessing
模塊就是跨平臺版本的多進程模塊。
multiprocessing
模塊提供了一個Process
類來代表一個進程對象
12、正則表達式中,如果直接給出字符,就是精確匹配。用\d
可以匹配一個數字,\w
可以匹配一個字母或數字
.
可以匹配任意字符
要匹配變長的字符,在正則表達式中,用*
表示任意個字符(包括0個),用+
表示至少一個字符,用?
表示0個或1個字符,用{n}
表示n個字符,用{n,m}
表示n-m個字符
\d{3}\s+\d{3,8}
。
我們來從左到右解讀一下:
-
\d{3}
表示匹配3個數字,例如‘010‘
; -
\s
可以匹配一個空格(也包括Tab等空白符),所以\s+
表示至少有一個空格,例如匹配‘ ‘
,‘ ‘
等; -
\d{3,8}
表示3-8個數字,例如‘1234567‘
。
要做更精確地匹配,可以用[]
表示範圍,比如:
-
[0-9a-zA-Z\_]
可以匹配一個數字、字母或者下劃線; -
[0-9a-zA-Z\_]+
可以匹配至少由一個數字、字母或者下劃線組成的字符串,比如‘a100‘
,‘0_Z‘
,‘Py3000‘
等等; -
[a-zA-Z\_][0-9a-zA-Z\_]*
可以匹配由字母或下劃線開頭,後接任意個由一個數字、字母或者下劃線組成的字符串,也就是Python合法的變量; -
[a-zA-Z\_][0-9a-zA-Z\_]{0, 19}
更精確地限制了變量的長度是1-20個字符(前面1個字符+後面最多19個字符)。
A|B
可以匹配A或B,所以(P|p)ython
可以匹配‘Python‘
或者‘python‘
。
^
表示行的開頭,^\d
表示必須以數字開頭。
$
表示行的結束,\d$
表示必須以數字結束。
你可能註意到了,py
也可以匹配‘python‘
,但是加上^py$
就變成了整行匹配,就只能匹配‘py‘
了。
我們強烈建議使用Python的r
前綴,就不用考慮轉義的問題了
除了簡單地判斷是否匹配之外,正則表達式還有提取子串的強大功能。用()
表示的就是要提取的分組(Group)。比如:
^(\d{3})-(\d{3,8})$
分別定義了兩個組,可以直接從匹配的字符串中提取出區號和本地號碼:
除了簡單地判斷是否匹配之外,正則表達式還有提取子串的強大功能。用()
表示的就是要提取的分組(Group)。比如:
^(\d{3})-(\d{3,8})$
分別定義了兩個組,可以直接從匹配的字符串中提取出區號和本地號碼:
如果一個正則表達式要重復使用幾千次,出於效率的考慮,我們可以預編譯該正則表達式,接下來重復使用時就不需要編譯這個步驟了,直接匹配:
1 >>> import re 2 # 編譯: 3 >>> re_telephone = re.compile(r‘^(\d{3})-(\d{3,8})$‘) 4 # 使用: 5 >>> re_telephone.match(‘010-12345‘).groups() 6 (‘010‘, ‘12345‘) 7 >>> re_telephone.match(‘010-8086‘).groups() 8 (‘010‘, ‘8086‘)
13、
因為互聯網協議包含了上百種協議標準,但是最重要的兩個協議是TCP和IP協議,所以,大家把互聯網的協議簡稱TCP/IP協議。
通信的時候,雙方必須知道對方的標識,好比發郵件必須知道對方的郵件地址。互聯網上每個計算機的唯一標識就是IP地址,類似123.123.123.123
。如果一臺計算機同時接入到兩個或更多的網絡,比如路由器,它就會有兩個或多個IP地址,所以,IP地址對應的實際上是計算機的網絡接口,通常是網卡。
IP協議負責把數據從一臺計算機通過網絡發送到另一臺計算機。數據被分割成一小塊一小塊,然後通過IP包發送出去。由於互聯網鏈路復雜,兩臺計算機之間經常有多條線路,因此,路由器就負責決定如何把一個IP包轉發出去。IP包的特點是按塊發送,途徑多個路由,但不保證能到達,也不保證順序到達。
14、
一個IP包除了包含要傳輸的數據外,還包含源IP地址和目標IP地址,源端口和目標端口。
端口有什麽作用?在兩臺計算機通信時,只發IP地址是不夠的,因為同一臺計算機上跑著多個網絡程序。一個IP包來了之後,到底是交給瀏覽器還是QQ,就需要端口號來區分。每個網絡程序都向操作系統申請唯一的端口號,這樣,兩個進程在兩臺計算機之間建立網絡連接就需要各自的IP地址和各自的端口號。
一個進程也可能同時與多個計算機建立鏈接,因此它會申請很多端口。
創建Socket
時,AF_INET
指定使用IPv4協議,如果要用更先進的IPv6,就指定為AF_INET6
。SOCK_STREAM
指定使用面向流的TCP協議,這樣,一個Socket
對象就創建成功,但是還沒有建立連接。建立TCP連接後,我們就可以向新浪服務器發送請求,要求返回首頁的內容:
# 發送數據: s.send(b‘GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n‘)
# 接收數據: buffer = [] while True: # 每次最多接收1k字節: d = s.recv(1024) if d: buffer.append(d) else: break data = b‘‘.join(buffer)
接收數據時,調用recv(max)
方法,一次最多接收指定的字節數,因此,在一個while循環中反復接收,直到recv()
返回空數據,表示接收完畢,退出循環。
當我們接收完數據後,調用close()
方法關閉Socket,這樣,一次完整的網絡通信就結束了:
用TCP協議進行Socket編程在Python中十分簡單,對於客戶端,要主動連接服務器的IP和指定端口,對於服務器,要首先監聽指定端口,然後,對每一個新的連接,創建一個線程或進程來處理。通常,服務器程序會無限運行下去。
同一個端口,被一個Socket綁定了以後,就不能被別的Socket綁定了。
UDP的使用與TCP類似,但是不需要建立連接。此外,服務器綁定UDP端口和TCP端口互不沖突,也就是說,UDP的9999端口與TCP的9999端口可以各自綁定。
15、為了便於程序保存和讀取數據,而且,能直接通過條件快速查詢到指定的數據,就出現了數據庫(Database)這種專門用於集中存儲和查詢的軟件
表是數據庫中存放關系數據的集合,一個數據庫裏面通常都包含多個表,比如學生的表,班級的表,學校的表,等等。表和表之間通過外鍵關聯。
要操作關系數據庫,首先需要連接到數據庫,一個數據庫連接稱為Connection;
連接到數據庫後,需要打開遊標,稱之為Cursor,通過Cursor執行SQL語句,然後,獲得執行結果。
Python定義了一套操作數據庫的API接口,任何數據庫要連接到Python,只需要提供符合Python標準的數據庫驅動即可。
# 導入SQLite驅動: >>> import sqlite3 # 連接到SQLite數據庫 # 數據庫文件是test.db # 如果文件不存在,會自動在當前目錄創建: >>> conn = sqlite3.connect(‘test.db‘) # 創建一個Cursor: >>> cursor = conn.cursor() # 執行一條SQL語句,創建user表: >>> cursor.execute(‘create table user (id varchar(20) primary key, name varchar(20))‘) <sqlite3.Cursor object at 0x10f8aa260> # 繼續執行一條SQL語句,插入一條記錄: >>> cursor.execute(‘insert into user (id, name) values (\‘1\‘, \‘Michael\‘)‘) <sqlite3.Cursor object at 0x10f8aa260> # 通過rowcount獲得插入的行數: >>> cursor.rowcount 1 # 關閉Cursor: >>> cursor.close() # 提交事務: >>> conn.commit() # 關閉Connection: >>> conn.close()
1 >>> conn = sqlite3.connect(‘test.db‘) 2 >>> cursor = conn.cursor() 3 # 執行查詢語句: 4 >>> cursor.execute(‘select * from user where id=?‘, (‘1‘,)) 5 <sqlite3.Cursor object at 0x10f8aa340> 6 # 獲得查詢結果集: 7 >>> values = cursor.fetchall() 8 >>> values 9 [(‘1‘, ‘Michael‘)] 10 >>> cursor.close() 11 >>> conn.close()
使用Python的DB-API時,只要搞清楚Connection
和Cursor
對象,打開後一定記得關閉,就可以放心地使用。
使用Cursor
對象執行insert
,update
,delete
語句時,執行結果由rowcount
返回影響的行數,就可以拿到執行結果。
使用Cursor
對象執行select
語句時,通過featchall()
可以拿到結果集。結果集是一個list,每個元素都是一個tuple,對應一行記錄。
在Python中操作數據庫時,要先導入數據庫對應的驅動,然後,通過Connection
對象和Cursor
對象操作數據。
要確保打開的Connection
對象和Cursor
對象都正確地被關閉,否則,資源就會泄露。
如何才能確保出錯的情況下也關閉掉Connection
對象和Cursor
對象呢?請回憶try:...except:...finally:...
的用法。
廖雪峰python摘錄二輪2