當前位置:
首頁 > 最新 > Python函數基本講解

Python函數基本講解

函數

函數返回: 函數執行時遇到return即執行完畢, 返回結果; 沒有return, 執行完畢返回None; return None可以簡寫為return.

內建函數: 高效快速, e.g. input(), ord(), pow(), isinstance(), iter()

局部變數: 比全局變數快, 盡量避免global; 但下文有提到全局字典保存不同線程專屬對象的技巧

默認參數: 必須指向不變對象: 函數在定義時, 默認參數值就會被計算出來

Python解釋器: 只檢查參數個數, 不檢查參數類型. 但它會檢查自己的內置函數參數格式和類型, 這不公平...對於自己寫的函數, 可以用內置函數isinstance實現參數類型檢查

1 if not isinstance(x, (int)):2 raise TypeError("not int type")

多返回值: return x, y, 當然本質是省略了括弧的tuple

多重賦值: x,y = y,x 要比三條語句執行快

字元串連接: 和Java語法一樣, 使用join()要比"+"高效, "+"會創建一個新的字元串並複製內容

延遲載入: ipmort在需要時再導入

無限循環: while 1比while True高效, 因為while 1是單步運算

列表生成式(list comprehension): 取代for和while更高效, 因為Python解釋器能夠在循環中發現它是一個可以預測的模式而被優化, 同時list更具可讀性. 和Java一樣, 可以在循環中節省一個額外的計數變數. e.g. evens = [i for i in range(10) if i%2 == 0] 要比 while i

生成器(generator):

里提到, 生成器保存的是演算法, 每次調用next()時才會計算出下一個值, 返回後保存這次計算的位置.

里也給出了逐個位元組發送視頻流的例子: chunk=(1000*i for i in xrange(1000)), chunk.next(), chunk.next(). 從兩個例子中可以看出生成器的共性: 是一個表達式, 再調用的時候才執行一次, 再次調用時執行第二次...而構造生成器, 要麼是一個xrange表達式, 要麼是在函數里合適的位置加入yield, 保存當前狀態後返回本次計算結果

查找效率: dict和set的查找很快, 因為是哈希表實現. list的實現是數組, 所以在list前插入效率不高, 因為list的後續下標不得不全部改變. 類比到Java里的集合框架: ArrayList數組線性表不適合在中間插入, 因為要改變後續元素下標; Java的Set和Python的set一樣都是元素不重複的, 而且由Hash實現. Java的集合框架太強大太繁雜, Python則簡化了很多, 記住dict, set, list, tuple目前就夠用了

裝飾器: 目前只掌握了簡單修飾, 加參數修飾的用法

進程和線程的基礎知識

CPU執行代碼是順序執行, 單核CPU通過讓任務交替執行, "模擬"除了多任務並發執行. 真正的多任務並發, 是在多核CPU上, 每個CPU負責執行一個任務. 但實際任務數量遠多於CPU核心數量, 所以最終還是操作系統把多任務輪流調度到不同的核心上執行.

進程/線程和物理內存(寄存器)/CPU的關聯: 函數調用, 會在棧中分配一塊空間, 存放局部變數和參數, 調用結束, 棧空間被釋放. 每個線程都有獨立的棧, 寄存器. 同一進程里的所有線程共享文件, 代碼和數據.

進程獨立性: 以CPU中的程序計數器PC為例, 物理的PC只有一個, 但是每個進程有獨立的邏輯PC, 保存了程序運行中的值, 但CPU輪到該進程時, 就將邏輯PC值複製到物理PC.

Python跨平台支持多進程

Unix/Linux平台Python支持fork(): 父進程複製出一個子進程

Windows平台Python支持Process(target,args): "模擬出fork()的效果", 即父進程所有Python對象pickle後傳遞給子進程

Python進程間通信的數據交換方式

multiprocessing模塊提供的Queue, Pipes

Python多線程基礎知識

標準庫提供了兩個模塊: thread和threading(推薦).

任務進程都默認啟動一個叫做MainThread的主線程, 主線程可以啟動新的子線程.

多線程的變數鎖: 高級語言的一條語句在CPU執行時是若干條語句, 計算中的結果會存入臨時變數中, 每個線程都有自己的臨時變數.

獲取鎖後一定要釋放, 否則等待鎖的線程會一直阻塞下去, 成為死線程. Python中可以通過try...finally確保釋放.

鎖的壞處: (1) 加鎖的代碼只能以單線程模式執行, 阻止了多線程並發, 降低效率; (2) 可能造成死鎖.

Python的GIL鎖導致多線程不能充分利用多核CPU

Python的解釋器有一個GIL(Global Interpreter Lock)鎖, 任何線程執行前, 先獲得GIL鎖, 每執行100行代碼, 解釋器會自動釋放GIL鎖, 讓別的線程有機會執行. 所以即使100個線程跑在100核CPU上, 也只能用到1個核.

GIL是Python解釋器設計的歷史遺留問題, 使得Python不能有效利用多核, 但可以通過多進程實現.

多個Python進程有各自獨立的GIL鎖, 互不影響.

GIL: 只是CPython解釋器存在. "GIL會序列化你的所有線程". 可以使用線程來管理多個派生進程, 使這些進程獨立運行於Python代碼之外.

多線程下選擇加鎖的全局變數or不得不傳遞下去的局部變數?

加鎖和傳遞局部變數都是老方法了, 只是局部變數傳遞起來很麻煩. 如果遇到每個線程一個專屬對象, 不能使用全局變數的情況, 作者就想到用一個全局dict, 但是通過線程自身作為key獲得保存的對應的對象, 例如:

1 # -*-coding:utf-8 -*- 2 import time, threading 3 4 global_dict = {} 5 6 class Student(object): 7 8 def __init__(self, name): 9 self.name = name10 11 def return_name(self):12 return self.name13 14 15 def do_task_1():16 std = Student("Elsa")17 global_dict[threading.currentThread()] = std18 std = global_dict[threading.current_thread()]19 print "thread %s, do_task_1() std.name = %s
" % (threading.currentThread().name, std.return_name())20 21 def do_task_2():22 std = Student("Anna")23 global_dict[threading.currentThread()] = std24 std = global_dict[threading.current_thread()]25 print "thread %s, do_task_2() std.name = %s
" % (threading.currentThread().name, std.return_name())26 27 28 if __name__ == "__main__":29 print "thread %s is running..." % threading.currentThread().name30 t1 = threading.Thread(target=do_task_1, name="t1")31 t2 = threading.Thread(target=do_task_2, name="t2")32 t1.start()33 t2.start()34 t1.join()35 t2.join()36 print "thread %s ended." % threading.currentThread().name

global_dict = {}作為一個全局變數, 保存了線程和它的專屬對象. 運行結果:

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python 的精彩文章:

阿里Python牛逼程序員:這5大技術即將顛覆2018年!

TAG:Python |