python基礎


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綜合性能比較優秀
dict:  本質上是順序表,不過每個元素存儲位置的角標,不是又插入順序決定的,而是由key經過hash算法和其他機制,動態生成的,即key通過hash散列,生成value應該存儲的位置,然后再去存儲這個value;所以dict的查詢時間復雜度是o(1);因此,dict的key只能為可hash的對象,即不可變類型;

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的實現是線程不安全?為什么?

展開 是不安全的,具體情況要分類討論。

單核情況下:

單核情況下——線程不安全

解釋:

  1. 到第5步的時候,可能這個時候python正好切換了一次GIL(據說python2.7中,每100條指令會切換一次GIL),執行的時間到了,被要求釋放GIL,這個時候thead 1的count=0並沒有得到執行,而是掛起狀態,count=0這個上下文關系被存到寄存器中.
  2. 然后到第6步,這個時候thead 2開始執行,然后就變成了count = 1,返回給count,這個時候count=1.
  3. 然后再回到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):排序,默認升序

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM