Python
- 什么是 Python 生成器?
- 什么是 Python 迭代器?
- list 和 tuple 有什么區別?
- Python 中的 list 和 dict 是怎么實現的?
- Python 中使用多線程可以達到多核CPU一起使用嗎?
- 什么是裝飾器?
- Python 如何進行內存管理?
- Python 中的垃圾回收機制?
- 什么是 lambda 表達式?
- 什么是深拷貝和淺拷貝?
- 雙等於和 is 有什么區別?
- 其它 Python 知識點
- 參考
什么是 Python 生成器?
generator,有兩種產生生成器對象的方式:一種是列表生成式加括號:
g1 = (x for x in range(10))
一種是在函數定義中包含yield
關鍵字:
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
return 'done'
g2 = fib(8)
對於generator對象g1和g2,可以通過next(g1)
不斷獲得下一個元素的值,如果沒有更多的元素,就會報錯StopIteration
也可以通過for循環獲得元素的值。
生成器的好處是不用占用很多內存,只需要在用的時候計算元素的值就行了。
什么是 Python 迭代器?
Python中可以用於for循環的,叫做可迭代Iterable
,包括list/set/tuple/str/dict等數據結構以及生成器;可以用以下語句判斷一個對象是否是可迭代的:
from collections import Iterable
isinstance(x, Iterable)
迭代器Iterator
,是指可以被next()
函數調用並不斷返回下一個值,直到StopIteration
;生成器都是Iterator,而列表等數據結構不是;可以通過以下語句將list變為Iterator:
iter([1,2,3,4,5])
生成器都是Iterator,但迭代器不一定是生成器。
list 和 tuple 有什么區別?
- list 長度可變,tuple不可變;
- list 中元素的值可以改變,tuple 不能改變;
- list 支持
append
;insert
;remove
;pop
等方法,tuple 都不支持
Python 中的 list 和 dict 是怎么實現的?
List: 本質是順序表,只不過每次表的擴容都是指數級,所以動態增刪數據時,表並不會頻繁改變物理結構,同時受益於順序表遍歷的高效性(通過角標配合表頭物理地址,計算目標元素的位置),使得python的list綜合性能比較優秀Python 中使用多線程可以達到多核CPU一起使用嗎?
Python中有一個被稱為Global Interpreter Lock(GIL)的東西,它會確保任何時候你的多個線程中,只有一個被執行。線程的執行速度非常之快,會讓你誤以為線程是並行執行的,但是實際上都是輪流執行。經過GIL這一道關卡處理,會增加執行的開銷。
可以通過多進程實現多核任務。
GIL(Global Interpreter Lock)
全局解釋器鎖
全局解釋器鎖(Global Interpreter Lock)是計算機程序設計語言解釋器用於同步線程的一種機制,它使得任何時刻僅有一個線程在執行。即便在多核處理器上,使用 GIL 的解釋器也只允許同一時間執行一個線程,常見的使用 GIL 的解釋器有CPython與Ruby MRI。可以看到GIL並不是Python獨有的特性,是解釋型語言處理多線程問題的一種機制而非語言特性。
GIL的設計初衷?
單核時代高效利用CPU, 針對解釋器級別的數據安全(不是thread-safe 線程安全)。 首先需要明確的是GIL並不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。當Python虛擬機的線程想要調用C的原生線程需要知道線程的上下文,因為沒有辦法控制C的原生線程的執行,所以只能把上下文關系傳給原生線程,同理獲取結果也是線 程在python虛擬機這邊等待。那么要執行一次計算操作,就必須讓執行程序的線程組串行執行。為什么要加在解釋器,而不是在其他層?
展開 GIL鎖加在解釋器一層,也就是說Python調用的Cython解釋器上加了GIL鎖,因為你python調用的所有線程都是原生線程。原生線程是通過C語言提供原生接口,相當於C語言的一個函數。你一調它,你就控制不了了它了,就必須等它給你返回結果。只要已通過python虛擬機 ,再往下就不受python控制了,就是C語言自己控制了。加在Python虛擬機以下加不上去,只能加在Python解釋器這一層。GIL的實現是線程不安全?為什么?
展開 是不安全的,具體情況要分類討論。單核情況下:
解釋:
- 到第5步的時候,可能這個時候python正好切換了一次GIL(據說python2.7中,每100條指令會切換一次GIL),執行的時間到了,被要求釋放GIL,這個時候thead 1的count=0並沒有得到執行,而是掛起狀態,count=0這個上下文關系被存到寄存器中.
- 然后到第6步,這個時候thead 2開始執行,然后就變成了count = 1,返回給count,這個時候count=1.
- 然后再回到thead 1,這個時候由於上下文關系,thead 1拿到的寄存器中的count = 0,經過計算,得到count = 1,經過第13步的操作就覆蓋了原來的count = 1的值,所以這個時候count依然是count = 1,所以這個數據並沒有保護起來。
python2.x和3.x都是在執行IO操作的時候,強制釋放GIL,使其他線程有機會執行程序。
Python2.x Python使用計數器ticks計算字節碼,當執行100個字節碼的時候強制釋放GIL,其他線程獲取GIL繼續執行。ticks可以看作是Python自己的計數器,專門作用於GIL,釋放后歸零,技術可以調整。
Python3.x Python使用計時器,執行時間達到閾值后,當前線程釋放GIL。總體來說比Python3.x對CPU密集型任務更好,但是依然沒有解決問題。
多核情況下:
多個CPU情況下,單個CPU釋放GIL鎖,其他CPU上的線程也會競爭,但是CPU-A可能又馬上拿到了GIL,這樣其他CPU上的線程只能繼續等待,直到重新回到待調度狀態。造成多線程在多核CPU情況下,效率反而會下降,出現了大量的資源浪費。
什么是裝飾器?
Python 中的垃圾回收機制?
https://www.jianshu.com/p/1e375fb40506
引用計數為0
分代回收
標記清除
什么是 lambda 表達式?
簡單來說,lambda表達式通常是當你需要使用一個函數,但是又不想費腦袋去命名一個函數的時候使用,也就是通常所說的匿名函數。
lambda表達式一般的形式是:關鍵詞lambda后面緊接一個或多個參數,緊接一個冒號“:”,緊接一個表達式
什么是深拷貝和淺拷貝?
賦值(=),就是創建了對象的一個新的引用,修改其中任意一個變量都會影響到另一個。
淺拷貝 copy.copy:創建一個新的對象,但它包含的是對原始對象中包含項的引用(如果用引用的方式修改其中一個對象,另外一個也會修改改變)
深拷貝:創建一個新的對象,並且遞歸的復制它所包含的對象(修改其中一個,另外一個不會改變){copy模塊的deep.deepcopy()函數}
雙等於和 is 有什么區別?
==
比較的是兩個變量的 value,只要值相等就會返回True
is
比較的是兩個變量的 id,即id(a) == id(b)
,只有兩個變量指向同一個對象的時候,才會返回True
但是需要注意的是,比如以下代碼:
a = 2
b = 2
print(a is b)
按照上面的解釋,應該會輸出False,但是事實上會輸出True,這是因為Python中對小數據有緩存機制,-5~256之間的數據都會被緩存。
其它 Python 知識點
類型轉換
- list(x)
- str(x)
- set(x)
- int(x)
- tuple(x)
try...except
list
lst[a:b]
:左閉右開lst.append(value)
:在末尾添加元素,復雜度O(1)lst.pop()
:彈出列表末尾元素,復雜度O(1)lst.pop(index)
:彈出任意位置元素,將后面的元素前移,復雜度O(n)lst.insert(index, value)
:插入元素,后面的元素后移,復雜度O(n)lst.remove(value)
:移除等於value的第一個元素,后面的元素前移,復雜度O(n)lst.count(value)
:計數值為value的元素個數lst.sort(reverse = False)
:排序,默認升序