python 學習筆記html
PYTHON-進階-魔術方法小結(方法運算符重載)
python有着像C++類似的運算符重載,只須要在類中重寫__add__、sub 等方法,就能夠直接對對象進行 + - 等操做,就好像內置對象同樣。也能夠重寫__getattr__、__setattr__等方法來操做屬性,init、del、str 、__len__等基本方法均可以重載,比較符的重載包括cmp、lt、__gt__等,以及getitem、__setitem__等操做索引的方法。總之,徹底能夠經過重載將一個類寫的python之父都不認得。python
傳統咱們實現消費者 生產者模型 都是須要經過多線程之間 互相協做,一個線程生產,一個線程消費。協程就是將多線程的事情在一個線程裏面幹了。傳統咱們一個函數老是一個入口,一個出口,很明確。協程就是容許一個子程序內部能夠中斷,而後執行其餘子程序,在適當的時候再返回來繼續執行,即便在while True這樣的函數。這樣的話咱們就能夠生產 後就調用消費者,而後消費後再繼續生產。
python 2.x中實現協程的方式 是經過generator,yield 中止一個子程序後,能夠經過send(xxx) 繼續執行
協程的好處:sql
極高的效率,不須要操做系統切換線程,只是程序內部控制segmentfault
由於協程是一個線程執行,那怎麼利用多核CPU呢?最簡單的方法是多進程+協程,既充分利用多核,又充分發揮協程的高效率,可得到極高的性能。數組
經過協程也能夠將一個複雜的程序分紅多個相互協做的子程序,就好像unix 的管道同樣,read ---> grep ---> print 這樣,程序會更簡單明瞭,複用性更高多線程
# 簡潔高效的生產消費模式 # 裝飾器, 用來自動啓動協程 def coroutine(func): def go(*args, **kwargs): g = func(*args, **kwargs) g.send(None) return g return go @coroutine def consumer(): r = '' while True: n = yield r if not n: return print ('consume {}'.format(n)) time.sleep(1) r = '200 OK' def producer(target): while True: n = random.randint(1, 100) print ('produce {}'.format(n)) r = target.send(n) print ('consumer return {}'.format(r)) if __name__ == '__main__': producer(consumer())
generator 跟普通的方法差很少,只是能夠經過yield返回多個值,以下閉包
def gen(): yield 1 yield 2 g = gen() print g # 輸出<generator object gen at 0x00000000026BCFC0> print g.next() # 輸出1 print g.next() # 輸出2 print g.next() # StopIteration 異常
經過dir(g) 能夠看到generator 實現了__iter__ 和 next 方法,因此generator 也是一個迭代器, 生成器能夠用來簡化迭代器的實現app
# 簡化版的小寫字母生成器,相對自定義迭代器來講,真的簡單多了 def lower_letters(): current = 'a' while current <= 'z': yield current current = chr(ord(current) + 1) for letter in lower_letters(): print letter
python 中的for in語法,能夠遍歷iterable 對象,例如list,array,map等,其實就是用iter()構造了一個iterator,並捕獲StopIteration異常dom
_iter = iter(iterableObj) while True: try: x = _iter.next() except StopIteration: break do_your_func(x)
iterator 和 iterable 本質上的區別是 iterator 實現了__next__(python2.x 是next)和 __iter__方法,而iterable只實現了__iter__方法,經過dir([1,2]) 能夠看到數組中只實現了iter ,只是iterable對象函數
因此,咱們能夠自定義一個迭代器,只要實現了__next__ 和 iter,而且__next__ 拋出StopIteration異常
class UpperLetter(object): def __init__(self): self.current = 'A' def next(self): if self.current > 'Z': raise StopIteration() result = self.current self.current = chr(ord(self.current) + 1) return result def __iter__(self): return self letters = UpperLetter() # 自定義的迭代器能夠經過for in遍歷哦 for letter in letters: print (letter)
約定這是python中的特殊方法,一般你將覆蓋這些方法,實現所需的功能,例如__init__
約定這是私有方法,外部不能訪問。
對於解釋器來講, from <模塊 包名> import * 是沒法導入以_開頭的方法
python都對象之間賦值都是拷貝引用,若是要拷貝對象,須要使用copy模塊
userInfo = { "resultCode": 0, "users": { 'name': "jobs" } } a = userInfo b = copy.copy(userInfo) c = copy.deepcopy(userInfo) # 修改原來的對象 userInfo['resultCode'] = 1 userInfo['users']['name'] = 'ryan' print a print b print c # 輸出: # {'resultCode': 1, 'users': {'name': 'ryan'}} # {'resultCode': 0, 'users': {'name': 'ryan'}} # {'resultCode': 0, 'users': {'name': 'jobs'}}
能夠看到,傳引用的全改了,淺拷貝的話子對象仍是保留着原來的引用,因此子對象跟着改了,深拷貝巋然不動!
切片是python一個頗有趣的語法糖,提供了簡潔的方法來取一個數組的一部分
L=[1, 2, 3,4,5] print L[1: 3] # 取索引1到索引3,不包含索引3 print L[:] # 第一個參數不填表示第一位,最後一個參數不填表示取最後一位 print L[-2:] # 負數表示倒數第n個元素 print L[::2] # 最後一個參數表示隔幾個取一次,跟range相似 # 輸出 # [2, 3] # [1, 2, 3, 4, 5] # [4, 5] # [1, 3, 5] # 優雅地將數組 進行分組操做,這裏若是i + 3大於數組len, 切片也能自動切到最後一截哈哈 for i in range(0, len(L), 3): print L[i: i+3] # 輸出 # [1, 2, 3] # [4, 5]
父函數中返回的函數引用了父函數中的參數或者局部變量,這個函數就稱爲閉包。所以,閉包也是攜帶狀態的函數,而且它的狀態能夠對外隱藏起來。閉包之於父函數就好像實例之於類
通常來講閉包不能修改外部變量,由於對於同名變量的修改 python 把它看作了 子函數中的 局部變量,因此你直接修改會返回UnboundLocalError: local variable 'm' referenced before assignment錯誤(聽說在python3中能夠用nolocal 聲明外部變量便可修改)
閉包函數相對於普通函數多出了一個__closure__屬性,這是一個元組,這個元祖裏面包含了全部閉包函數中引用到的外部變量
參考:
說說 Python 中的閉包
裝飾器就是一種特殊的閉包,經過傳遞一個待修飾的函數 給 修飾函數,並返回一個修飾後的函數,能夠用@decoratorFunc 來簡化語法,裝飾器的做用就是爲已經存在的函數或對象添加額外的功能
參考:
Python 的閉包和裝飾器:https://segmentfault.com/a/1190000004461404
詳解 Python 的裝飾器
[ x * x for x in list] & { x[a] : x[b] for x in list}
var = 1 def func1(): # 此處聲明 global var 解決 var = var + 1 return var print func1() # 報錯UnboundLocalError: local variable 'var' referenced before assignment # 由於咱們企圖在內層改變外層的var,因此python就把他當作局部變量了,那很明顯,這個var在局部並無賦值
用with as 字句代替以前的try .... finally fs.close()之類的語句方便了不少
tuple 就是不可變的list, 用(1, 2, 3)定義
**若是要定義只有一個元素的tuple,不能定義爲(1),python把他當作數字1(括號運算),正確的寫法爲(1, )
python 的默認參數若是爲數組的話,要千萬當心了
def func2(array=[]): array.append("haha") print array func2() # 輸出 ['haha'] func2() # 輸出 ['haha', 'haha']
爲何每次調用的結果都不同? 由於默認參數的值在函數定義的時候就生成好了,因此當默認參數指向的是 可變對象如list
就會改變它的值,因此**默認參數必定要是不可變對象"",如str,int等
要往可變參數中傳入一個數組,有兩種方法
def func3(*arr): for i in arr: print i nums = [1, 2, 3] func3(nums[0], nums[1], nums[2]) # 不得不說,這種寫法巨醜 func3(*nums) # 好看
可變參數在方法中就是一個tuple,而關鍵字參數在方法中就是一個dict,它能夠擴展函數的功能,讓用戶自定義配置,例如sqlalchemy的create_engine(*args, kwargs)就使咱們能夠根據須要定義encoding,echo等參數
一樣的,要傳入一個dict給可變參數的方法能夠用**dict**
星解提供了一個很藝術化的方法來unpack一個list或dict
def test(x, y, z): print(x, y, z) testDict = {'x': 1, 'y': 2, 'z': 3} testList = [10, 20, 30] test(*testDict) test(*testList) #1-> x y z #2-> 10 20 30