Python學習 讀書筆記(1)
1.
轉義字元必須 ,’\n’,來和變數分開
2.
當Python直譯器讀取原始碼時,為了讓它按UTF-8編碼讀取,檔案開頭寫上這兩行:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
第一行註釋是為了告訴Linux/OS X系統,這是一個Python可執行程式,Windows系統會忽略這個註釋;
第二行註釋是為了告訴Python直譯器,按照UTF-8編碼讀取原始碼,否則,你在原始碼中寫的中文輸出可 能會有亂碼。
3.
tuple的陷阱:當你定義一個tuple時,在定義的時候,tuple的元素就必須被確定下來,比如:
>>> t = (1, 2)
>>> t
(1, 2)
如果要定義一個空的tuple,可以寫成():
>>> t = ()
>>> t
()
但是,要定義一個只有1個元素的tuple,如果你這麼定義:
>>> t = (1)
>>> t
1
定義的不是tuple,是1這個數!這是因為括號()既可以表示tuple,又可以表示數學公式中的小括號,這就產生了歧義,因此,Python規定,這種情況下,按小括號進行計算,計算結果自然是1。
所以,只有1個元素的tuple定義時必須加一個逗號,,來消除歧義:
>>> t = (1,)
>>> t
(1,)
Python在顯示只有1個元素的tuple時,也會加一個逗號,,以免你誤解成數學計算意義上的括號。
4.
dict的key必須是不可變物件。
這是因為dict根據key來計算value的儲存位置,如果每次計算相同的key得出的結果不同,那dict內部就完全混亂了。這個通過key計算位置的演算法稱為雜湊演算法(Hash)。
要保證hash的正確性,作為key的物件就不能變。在Python中,字串、整數等都是不可變的,因此,可以放心地作為key。而list是可變的,就不能作為key:
>>> key = [1, 2, 3]
>>> d[key] = 'a list'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
5.不可變物件
str是不變物件,而list是可變物件。
對於可變物件,比如list,對list進行操作,list內部的內容是會變化的,比如:
>>> a = ['c', 'b', 'a']
>>> a.sort()
>>> a
['a', 'b', 'c']
而對於不可變物件,比如str,對str進行操作呢:
>>> a = 'abc'
>>> a.replace('a', 'A')
'Abc'
>>> a
'abc'
雖然字串有個replace()方法,也確實變出了’Abc’,但變數a最後仍是’abc’,應該怎麼理解呢?
我們先把程式碼改成下面這樣:
>>> a = 'abc'
>>> b = a.replace('a', 'A')
>>> b
'Abc'
>>> a
'abc'
要始終牢記的是,a是變數,而’abc’才是字串物件!有些時候,我們經常說,物件a的內容是’abc’,但其實是指,a本身是一個變數,它指向的物件的內容才是’abc’
6.
tuple雖然是不變物件,但試試把(1, 2, 3)和(1, [2, 3])放入dict或set中,並解釋結果。
>>>a = ('james', 'jordan', 'kobe')
>>>b = ('james' , ['jordan', 'kobe'])
>>>dict1={a:'籃球運動員'}
>>>dict1[a]
>>>'籃球運動員'
>>>dict2={b:'籃球運動員'}
>>>Traceback (most recent call last):
>>> File "<stdin>", line 1, in <module>
>>>TypeError: unhashable type: 'list'
#tuple a 所指向的三個人是不可變的的 他們都是籃球運動員
#tuple b 所指向的只有james是不可變的的 還有兩個人雖然是叫jordan kobe但不一定就是打籃球的 可能是踢足球的jordan和打乒乓球的kobe
7.
py內建的函式自帶有引數型別檢查,但是使用者自己定義的函式並沒有,這個時候需要自己利用isinstance()實現
def my_abs(x):
if not isinstance(x, (int, float)):
raise TypeError('bad operand type')
if x >= 0:
return x
else:
return -x
添加了引數檢查後,如果傳入錯誤的引數型別,函式就可以丟擲一個錯誤:
>>> my_abs('A')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in my_abs
TypeError: bad operand type
8.
預設引數很有用,但使用不當,也會掉坑裡。預設引數有個最大的坑,演示如下:
先定義一個函式,傳入一個list,新增一個END再返回:
def add_end(L=[]):
L.append('END')
return L
當你正常呼叫時,結果似乎不錯:
>>> add_end([1, 2, 3])
[1, 2, 3, 'END']
>>> add_end(['x', 'y', 'z'])
['x', 'y', 'z', 'END']
當你使用預設引數呼叫時,一開始結果也是對的:
>>> add_end()
['END']
但是,再次呼叫add_end()時,結果就不對了:
>>> add_end()
['END', 'END']
>>> add_end()
['END', 'END', 'END']
很多初學者很疑惑,預設引數是[],但是函式似乎每次都“記住了”上次添加了’END’後的list。
原因解釋如下:
Python函式在定義的時候,預設引數L的值就被計算出來了,即[],因為預設引數L也是一個變數,它指向物件[],每次呼叫該函式,如果改變了L的內容,則下次呼叫時,預設引數的內容就變了,不再是函式定義時的[]了。
所以,定義預設引數要牢記一點:預設引數必須指向不變物件!
要修改上面的例子,我們可以用None這個不變物件來實現:
def add_end(L=None):
if L is None:
L = []
L.append('END')
return L
現在,無論呼叫多少次,都不會有問題:
>>> add_end()
['END']
>>> add_end()
['END']
為什麼要設計str、None這樣的不變物件呢?因為不變物件一旦建立,物件內部的資料就不能修改,這樣就減少了由於修改資料導致的錯誤。此外,由於物件不變,多工環境下同時讀取物件不需要加鎖,同時讀一點問題都沒有。我們在編寫程式時,如果可以設計一個不變物件,那就儘量設計成不變物件。
9.
Python內建的enumerate函式可以把一個list變成索引-元素對,這樣就可以在for迴圈中同時迭代索引和元素本身:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C
(用逗號新增一個引數1可以使標號從1開始)
10.閉包
注意到返回的函式在其定義內部引用了局部變數args,所以,當一個函式返回了一個函式後,其內部的區域性變數還被新函式引用,所以,閉包用起來簡單,實現起來可不容易。
另一個需要注意的問題是,返回的函式並沒有立刻執行,而是直到呼叫了f()才執行。我們來看一個例子:
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
在上面的例子中,每次迴圈,都建立了一個新的函式,然後,把建立的3個函式都返回了。
你可能認為呼叫f1(),f2()和f3()結果應該是1,4,9,但實際結果是:
>>> f1()
9
>>> f2()
9
>>> f3()
9
原因就在於返回的函式引用了變數i,但它並非立刻執行。等到3個函式都返回時,它們所引用的變數i已經變成了3,因此最終結果為9。返回的其實是通過for in 迴圈生成的[f,f,f]這個函式list,但是函式並沒有執行,其實i已經變成3了
返回閉包時牢記的一點就是:返回函式不要引用任何迴圈變數,或者後續會發生變化的變數。
如果一定要引用迴圈變數怎麼辦?方法是再建立一個函式,用該函式的引數繫結迴圈變數當前的值,無論該迴圈變數後續如何更改,已繫結到函式引數的值不變:
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被執行,因此i的當前值被傳入f()
return fs
再看看結果:
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9