關於字典和列表的使用區別
和list比較,dict有以下幾個特點:尋找和插入的速度極快,不會隨著key的增加而變慢;需要佔用大量的記憶體,記憶體浪費多。而list相反:尋找和插入的時間隨著元素的增加而增加; (資料結構是鏈表式的和順序表形式的)佔用空間小,浪費記憶體很少。所以,dict是用空間來換取時間的一種方法。dict可以用在需要高速尋找的很多地方,在Python代碼中幾乎無處不在,正確使用dict非常重要,需要牢記的第一條就是dict的key必須是不可變對象。這是因為dict根據key來計算value的儲存位置,如果每次計算相同的key得出的結果不同,那dict內部就完全混亂了。這個通過key計算位置的演算法稱為雜湊演算法(Hash)。要保證hash的正確性,作為key的對象就不能變。在Python中,字串、整數等都是不可變的,因此,可以放心地作為key。而list是可變的,就不能作為key:
列表產生式 列表產生式即List Comprehensions,是Python內建的非常簡單卻強大的可以用來建立list的產生式。
普通產生式>>> [x * x for x in range(1, 11)][1, 4, 9, 16, 25, 36, 49, 64, 81, 100]加上if判斷 做為篩選>>> [x * x for x in range(1, 11) if x % 2 == 0][4, 16, 36, 64, 100後邊兩個可迭代對象 相當於for迴圈的嵌套>>> [m + n for m in 'ABC' for n in 'XYZ']['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
課後習題
輸入:L1 = ['Hello', 'World', 18, 'Apple', None]輸出:['hello', 'world', 'apple']測試:[x.lower() for x in l1 if isinstance(x, str)]
產生器
通過列表產生式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。
所以,如果列表元素可以按照某種演算法推算出來,那我們是否可以在迴圈的過程中不斷推算出後續的元素呢。這樣就不必建立完整的list,從而節省大量的空間。在Python中,這種一邊迴圈一邊計算的機制,稱為產生器:generator。
產生器的建立方式:
要建立一個generator,有很多種方法。第一種方法很簡單,只要把一個列表產生式的[]改成(),就建立了一個generator:>>> L = [x * x for x in range(10)]>>> L[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]>>> g = (x * x for x in range(10))>>> g<generator object <genexpr> at 0x1022ef630>調用產生器: 通過next(g) 來調用產生器, 返回需要的內容 迭代到最後一個元素的時候 會拋出 stopIteration 異常來結束迭代 >>> next(g) Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration使用for迴圈來迭代產生器>>> g = (x * x for x in range(10))>>> for n in g:... print(n)使用產生器來完成斐波納挈數列def fib(max): n, a, b = 0, 0, 1 while n < max: yield b a, b = b, a + b n = n + 1for i in fibZ(5): print i
產生器函數和普通函數的區別
generator是非常強大的工具,在Python中,可以簡單地把列表產生式改成generator,也可以通過函數實現複雜邏輯的generator。要理解generator的工作原理,它是在for迴圈的過程中不斷計算出下一個元素,並在適當的條件結束for迴圈。對於函數改成的generator來說,遇到return語句或者執行到函數體最後一行語句,就是結束generator的指令,for迴圈隨之結束。普通函數調用直接返回結果產生器函數調用返回的是一個產生器的對象
高階函數的簡單分析
一個函數可以接受另外一個函數做為參數, 這種函數就稱為高階函數
def add(x, y, f): return f(x) + f(y)x = -5y = 6f = absf(x) + f(y) ==> abs(-5) + abs(6) ==> 11return 11
內建函數
map 和 reduce 函數的使用:
map函數: 接收一個函數 和一個可迭代的對象 iterable (會依次的把後面的可迭代對象的元素作用於前面的函數上) 返回的是另外一個iterator 迭代器, 迭代器是惰性的 可減少記憶體空間的使用
簡單一實例:
>>> def f(x):... return x * x...>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])>>> list(r)[1, 4, 9, 16, 25, 36, 49, 64, 81]
再看reduce的用法。reduce把一個函數作用在一個序列[x1,x2,...]上,這個函數必須接收兩個參數,reduce把結果繼續和序列的下一個元素做累積計算,其效果就是:
>>> from functools import reduce>>> def add(x, y):... return x + y...>>> reduce(add, [1, 3, 5, 7, 9])
使用map和reduce 配合 把一個字串轉換成為一個整數
from functools import reducedef char2num(s): return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s] # 按照鍵來進行取值def str2int(s): return reduce(lambda x, y: x * 10 + y, map(char2num, s))
課後練習
利用map()函數,把使用者輸入的不規範的英文名字,變為首字母大寫,其他小寫規範名字。輸入:['adam', 'LISA', 'barT'],輸出:['Adam', 'Lisa', 'Bart']:def normalize(name): return name.lower().title()L1 = ['adam', 'LISA', 'barT']L2 = list(map(normalize, L1))print L2
利用map和reduce編寫一個str2float函數,把字串'123.456'轉換成浮點數123.456:def str2float(s): nums = map(lambda ch: CHAR_TO_FLOAT[ch], s) point = 0 def to_float(f, n): nonlocal point if n == -1: point = 1 return f if point == 0: return f * 10 + n else: point = point * 10 return f + n / point return reduce(to_float, nums, 0.0)print(str2float('0'))print(str2float('123.456'))print(str2float('123.45600'))print(str2float('0.1234'))print(str2float('.1234'))print(str2float('120.0034'))def demo(m):"""判斷是不是迴文數字""" n = list(str(m)) n.reverse() print n if "".join(n) == str(m): return Truefilter(demo, list)
sorted 排序的課後習題
按照名稱進行排序L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]sorted(L,key=lambda x:x[0]) 會把每個列表中的元素就行遍曆 然後傳遞給 key執行的函數 \dict.items() 返回的是一個列表 列表中的元素 是一個個的元組
高階函數 返回函數
函數的嵌套 會形成閉包的概念, 多次調用閉包 之間的結果是不受影響的
高階函數除了可以接受函數作為參數外,還可以把函數作為結果值返回。裝飾器就是特殊的高階函數 加上函數的嵌套來組合成的
但是在使用高階函數返回函數的時候 需要注意:
閉包注意到返回的函數在其定義內部引用了局部變數args,所以,當一個函數返回了一個函數後,其內部的局部變數還被新函數引用,所以,閉包用起來簡單,實現起來可不容易。另一個需要注意的問題是,返回的函數並沒有立刻執行,而是直到調用了f()才執行。我們來看一個例子:def count(): fs = [] for i in range(1, 4): def f(): return i*i fs.append(f) return fsf1, f2, f3 = count()在上面的例子中,每次迴圈,都建立了一個新的函數,然後,把建立的3個函數都返回了。你可能認為調用f1(),f2()和f3()結果應該是1,4,9,但實際結果是:>>> f1()9>>> f2()9>>> f3()9全部都是9。原因就在於返回的函數引用了變數i,但它並非立刻執行。等到3個函數都返回時,它們所引用的變數i已經變成了3,因此最終結果為9。返回閉包時牢記的一點就是:返回函數不要引用任何迴圈變數,或者後續會發生變化的變數。如果一定要引用迴圈變數怎麼辦。方法是再建立一個函數,用該函數的參數綁定迴圈變數當前的值,無論該迴圈變數後續如何更改,已綁定到函數參數的值不變: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 小結一個函數可以返回一個計算結果,也可以返回一個函數。返回一個函數時,牢記該函數並未執行,返回函數中不要引用任何可能會變化的變數
匿名函數中的一點
函數名稱的可以使用name 來擷取到函數的名稱
@functools.wraps(func) 使用 wraps(func) 來裝飾使用的內部的函數
完整版的裝飾器import functoolsdef log(func): @functools.wraps(func) def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
sys 模組的使用
sys模組有一個argv變數,用list儲存了命令列的所有參數。argv至少有一個元素,因為第一個參數永遠是該.py檔案的名稱 使用solt 累的屬性來顯示可能給累綁定的屬性
class Student(object): __slots__ = ('name', 'age') # 用tuple定義允許綁定的屬性名稱>>> s = Student() # 建立新的執行個體>>> s.name = 'Michael' # 綁定屬性'name'>>> s.age = 25 # 綁定屬性'age'>>> s.score = 99 # 綁定屬性'score'Traceback (most recent call last): File "<stdin>", line 1, in <module>AttributeError: 'Student' object has no attribute 'score'由於'score'沒有被放到__slots__中,所以不能綁定score屬性,試圖綁定score將得到AttributeError的錯誤。使用__slots__要注意,__slots__定義的屬性僅對當前類執行個體起作用,對繼承的子類是不起作用的:
mkvirtualenv.bat –python==C:\Python\Python36-32\python.exe py3 指定版本號碼的安裝 使用枚舉類 確定元類的使用方式 哪些地方會使用到元類
redismongodmysqlflaskdjangorestful-api單元測試文檔測試 def abs(n): ''' Function to get absolute value of number. Example: >>> abs(1) 1 >>> abs(-1) 1 >>> abs(0) 0 ''' return n if n >= 0 else (-n) 無疑更明確地告訴函數的調用者該函數的期望輸入和輸出。 並且,Python內建的“文檔測試”(doctest)模組可以直接提取注釋中的代碼並執行測試。
檔案讀寫的操作
for else 的使用: 當for語句正常執行的話, else 語句也就能正常的進行執行, 執行個體: for item in range(10): print(item) else: print("else") 這個語句是 會列印 出來 item的值 和 else for item in []: print(item) else: print ("else") 這個也會列印出來 else for item in range(10): print(item) if item == 10: break else: print ("else") 如果for迴圈被中斷了, 那麼這個else 也就不能再使用了
可以通過 os.access() 來查看當前檔案是不是有許可權, 去進行讀寫的操作 或者是 檔案是不是存在的調用read()會一次性讀取檔案的全部內容,如果檔案有10G,記憶體就爆了,所以,要保險起見,可以反覆調用read(size)方法,每次最多讀取size個位元組的內容。另外,調用readline()可以每次讀取一行內容,調用readlines()一次讀取所有內容並按行返回list。因此,要根據需要決定怎麼調用。如果檔案很小,read()一次性讀取最方便;如果不能確定檔案大小,反覆調用read(size)比較保險;如果是設定檔,調用readlines()最方便:for line in f.readlines(): print(line.strip()) # 把末尾的'\n'刪掉遇到有些編碼不規範的檔案,你可能會遇到UnicodeDecodeError,因為在文字檔中可能夾雜了一些非法編碼的字元。遇到這種情況,open()函數還接收一個errors參數,表示如果遇到編碼錯誤後如何處理。最簡單的方式是直接忽略: f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')