Python知識點整理


1.可迭代對象、迭代器、生成器

可迭代對象包括迭代器,序列,字典,支持innot in訪問對象中的元素。迭代器可以記住操作的位置,是可迭代對象的子集,而生成器又是迭代器的子集。

迭代器與其他可迭代對象的區別在於迭代器支持__next()__方法,可以通過iter()方法將可迭代對象轉化為迭代器。

生成器是迭代器的子集。

yield可以與函數組合成一個生成器,其作用有2個:1.類似於return,返回一個值,函數執行到此處退出。

2.可以記住每次遍歷的位置,再次調用函數,就從上次執行的yield的下一句開始執行。

生成器與迭代器相比,在提高運行速度的同時,大大降低了占用的內存。

可以看個例子:

def foo():
    print("starting...")
    while True:
        res = yield 4
        print("res:",res)
g = foo()
print(next(g))
print("*"*20)
print(next(g))

輸出:

starting...
4
********************
res: None
4

2.深拷貝與淺拷貝

在python中,對象賦值實際上是對象的引用。當創建一個對象,然后把它賦給另一個變量的時候,python並沒有拷貝這個對象,而只是拷貝了這個對象的引用。

判斷的法則是:看拷貝前后變量的內存地址是否發生變化

(1)直接賦值,默認淺拷貝傳遞對象的引用而已,原始列表改變,被賦值的b也會做相同的改變

>>> b=alist
>>> print b
[1, 2, 3, ['a', 'b']]
>>> alist.append(5)
>>> print alist;print b
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b'], 5]

img

(2)copy淺拷貝,沒有拷貝子對象,所以原始數據改變,子對象會改變,但注意僅僅是被拷貝的部分

>>> import copy

>>> c=copy.copy(alist)
>>> print alist;print c
[1, 2, 3, ['a', 'b']]
[1, 2, 3, ['a', 'b']]
>>> alist.append(5)
>>> print alist;print c
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]

>>> alist[3]
['a', 'b']
>>> alist[3].append('cccc')
>>> print alist;print c
[1, 2, 3, ['a', 'b', 'cccc'], 5]
[1, 2, 3, ['a', 'b', 'cccc']] 里面的子對象被改變了

img

(3)深拷貝,包含對象里面的自對象的拷貝,所以原始對象的改變不會造成深拷貝里任何子元素的改變

>>> import copy
>>> d=copy.deepcopy(alist)
>>> print alist;print d
[1, 2, 3, ['a', 'b']]
[1, 2, 3, ['a', 'b']]始終沒有改變
>>> alist.append(5)
>>> print alist;print d
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]始終沒有改變
>>> alist[3]
['a', 'b']
>>> alist[3].append("ccccc")
>>> print alist;print d
[1, 2, 3, ['a', 'b', 'ccccc'], 5]
[1, 2, 3, ['a', 'b']]  始終沒有改變

img

3.python下划線

  • 以單下划線開頭,表示這是一個保護成員,只有類對象和子類對象自己能訪問到這些變量。以單下划線開頭的變量和函數被默認當作是內部函數,使用from module improt *時不會被獲取,但是使用import module可以獲取

  • 以單下划線結尾僅僅是為了區別該名稱與關鍵詞

  • 雙下划線開頭,表示為私有成員,只允許類本身訪問,子類也不行。在文本上被替換為_class__method

  • 雙下划線開頭,雙下划線結尾。一種約定,Python內部的名字,用來區別其他用戶自定義的命名,以防沖突。是一些 Python 的“魔術”對象,表示這是一個特殊成員,例如:定義類的時候,若是添加__init__方法,那么在創建類的實例的時候,實例會自動調用這個方法,一般用來對實例的屬性進行初使化,Python不建議將自己命名的方法寫為這種形式。

4.方法

Python其實有3個方法,即靜態方法(staticmethod),類方法(classmethod)和實例方法,如下:

class A(object):
    '''
    實例方法:對於實例方法,我們知道在類里每次定義方法的時候都需要綁定這個實例,就是`foo(self, x)`,為什么     要這么做呢?因為實例方法的調用離不開實例,我們需要把實例自己傳給函數,調用的時候是這樣的`a.foo(x)`(其實     是`foo(a, x)`).
    '''
    def foo(self,x):
        print "executing foo(%s,%s)"%(self,x)
    '''
    類方法一樣,只不過它傳遞的是類而不是實例,`A.class_foo(x)`.注意這里的self和cls可以替換別的參數,但是     python的約定是這倆,還是不要改的好.
    '''
    @classmethod
    def class_foo(cls,x):
        print "executing class_foo(%s,%s)"%(cls,x)
    '''
    對於靜態方法其實和普通的方法一樣,不需要對誰進行綁定,唯一的區別是調用的時候需要使用`a.static_foo(x)`     或者`A.static_foo(x)`來調用.
    '''
    @staticmethod
    def static_foo(x):
        print "executing static_foo(%s)"%x

a=A()

5.為什么python不支持函數重載

函數重載主要是為了解決兩個問題。
1。可變參數類型。
2。可變參數個數。

對於情況 1 ,函數功能相同,但是參數類型不同,python 如何處理?答案是根本不需要處理,因為 python 可以接受任何類型的參數,如果函數的功能相同,那么不同的參數類型在 python 中很可能是相同的代碼,沒有必要做成兩個不同函數。

那么對於情況 2 ,函數功能相同,但參數個數不同,python 如何處理?大家知道,答案就是缺省參數。對那些缺少的參數設定為缺省參數即可解決問題。因為你假設函數功能相同,那么那些缺少的參數終歸是需要用的。

6.函數參數傳遞

對於不可變參數,例如數字,字符串,元組,類似於值傳值,函數內部產生一個副本,函數內對參數的操作,不會影響到函數外的參數。

對於可變參數,例如列表,集合,字典,類似於引用傳值,在函數內對參數的操作,在函數外部也會顯現出來,但是這種操作不包括重新創建一個對象的修改,這樣內存內置就會發生變化。

7.*args and **kwargs

表示可變長度的參數,其中*args表示位置參數,**kwargs表示關鍵字參數。

args因為 *前綴 的存在, 會把位置參數收集到一個tuple元組中。
kwargs因為 **前綴 的存在, 會把位置參數收集到一個dict字典中

當然 argskwargs可以為空

8.閉包

閉包又被稱為閉合函數,其特征是:一個函數(外函數)嵌套了另外一個函數(內函數),外函數返回了內函數的引用,而內函數使用了外函數的臨時變量。

當外函數執行時,會將外函數的臨時變量存儲下來供內函數使用,需要注意是,多次調用閉包函數,其內函數使用的都是一套外函數的臨時變量。可以使用閉包的 closure 屬性,來查看該臨時變量的地址。

def nth_power(exponent):
    def exponent_of(base):
        return base ** exponent
    return exponent_of
square = nth_power(2)
#查看 __closure__ 的值
print(square.__closure__)

************************************
(<cell at 0x0000014454DFA948: int object at 0x00000000513CC6D0>,)

為什么要使用閉包函數?

閉包函數的優勢主要在多次調用上,一般情況下,在確定具有某些功能的函數的時候,都需要先執行一些額外的工作,如果多次執行這個函數,那么這些額外的操作會執行多次,而使用閉包函數,就可以將這些額外操作放在外函數內,這樣閉包函數的多次調用,便會直接去提取外函數的這些變量。避免了不必要的開銷,提高了程序的運行效率。此外,表達也會更加簡潔。

9.裝飾器

裝飾器基於閉包,本質上是一個函數,其可以讓函數在不做任何代碼改動的情況下,增加其額外的功能。

def test(func):
    def wrapper():
        print("ok")
        return func()
    return wrapper

這是一個最簡單的裝飾器的例子, print("ok")是原本的功能,func()是拓展額外的功能。從這也可以看出,他的基本結構就是一個閉包。

@test
def func():
    print("yes")

func()
***************************
ok
yes

@符號是裝飾器的語法糖,等同於func=test(func),其避免了額外的賦值操作。

從以上的例子不難看出,裝飾器提高了程序的可重復利用性,並增加了程序的可讀性。

  • 帶參數的裝飾器

    def nth_power(exponent):
        def exponent_of(base):
            return base ** exponent
        return exponent_of
    
  • 類裝飾器

    相比於函數裝飾器,類裝飾器靈活度更大,封裝性更強。使用類裝飾器可以依靠內部的__call__方法

    class test(object):
        def __init__(self,func):
            self._func=func
        def __call__(self, *args, **kwargs):
            print("ok")
            self._func()
            print("yes")
            
    @test
    def foo():
        print("good")
    
    foo()
    
  • functools.wrap

    裝飾器有個問題就是func的函數的元信息會被wrapper取代,這些元信息包括__name__,docstring等

    這個時候可以使用functools.wrap,wraps本身也是一個裝飾器,其可以使得func的函數的元信息不會被wrapper取代。

    from  functools import wraps
    def test(func):
        @wraps(func)
        def wrapper():
            print("ok")
            return func()
        return wrapper
    @test
    def func():
        print("yes")
    
    print(func.__name__)   #加 @wraps返回func,不加 @wraps返回wrapper
    

10.read,readline和readlines

  • read 讀取整個文件
  • readline 讀取下一行,使用生成器方法
  • readlines 讀取整個文件到一個迭代器以供我們遍歷

11.內存管理

python的內存管理機制主要類似於金字塔模型

  • -1,-2層主要有操作系統進行操作;
  • 第0層是C中的malloc,free等內存分配和釋放函數進行操作;
  • 第1層和第2層是內存池,有Python的接口函數PyMem_Malloc函數實現,當對象小於256K時有該層直接分配內存;
  • 第3層是最上層,也就是我們對Python對象的直接操作; 

對第二層的python的內存池機制做一些說明:

由於頻繁創建大量消耗小內存的對象時,調用new/malloc會導致大量的內存碎片,致使效率降低,因此第1層和第2層的內存池,主要是為了處理小塊內存的申請和釋放,操作原理是先預先在內存中申請一定數量的,大小相等的內存塊留作備用,當有新的內存需求時,就先從內存池中分配內存給這個需求,不夠了之后再使用第0層的malloc申請256kb的內存,這樣做最顯著的優勢就是能夠減少內存碎片,提升效率。

釋放內存時,當一個對象的引用計數變為 0 時,python就會調用它的析構函數。調用析構函數並不意味着最終一定會調用free 釋放內存空間,如果真是這樣的話,那頻繁地申請、釋放內存空間會使 Python的執行效率大打折扣。因此在析構時也采用了內存池機制,從內存池申請到的內存會被歸還到內存池中,以避免頻繁地 釋放 動作。

img

12.異常處理

  • try…except...else

    s = 'Hello girl!'
    try:
        print( s[100])
    except:
         print('error')
    print ('continue')
    
    ************************
    '''
    try-except語句按照如下方式工作: 
    1)執行try子句(在關鍵字try和except之間的語句) 
    2)如果沒有異常發生,忽略except子句,try子句執行后結束 
    3)如果在執行try子句的過程中發生了異常,那么try子句余下的部分將被忽略;如果異常的類型和excet之后的名稱相符,那么對應的except子句將被執行;最后執行try語句之后的代碼 
    4)如果一個異常沒有與任何except匹配,那么這個異常將會傳遞給上層的try語句中
    
    一個try語句可以包含多個except子句,分別用來處理特定的異常,但是最多只有一個except子句分支會被執行
    
    一個except子句可以同時處理多個異常,這些異常將被放在圓括號里以元組形式出現
    
    except關鍵字后面甚至可以不加名稱(當通配符使用)
    
    另外,try-except語句還有一個可選的else子句,這個子句必須寫在except子句之后,當且僅當try子句沒有任何異常發生時執行(Phthonic的寫法)
    '''
    
  • try…except...finally

    簡而言之,finally語句就是用來做異常處理的掃尾工作的

    不管try子句中到底有沒有異常,finally子句都會執行

    如果一個異常在try子句中(或者except和else子句中)被拋出,而沒有任何的except把它截住,那么這個異常就會在fianly子句執行后再次被拋出

13.多線程、多進程

  • 多線程和多進程關系

    '''
    ”進程是資源分配的最小單位,線程是CPU調度的最小單位“
    
    做個簡單的比喻:進程=火車,線程=車廂
    
    1.線程在進程下行進(單純的車廂無法運行)
    2.一個進程可以包含多個線程(一輛火車可以有多個車廂)
    3.不同進程間數據很難共享(一輛火車上的乘客很難換到另外一輛火車,比如站點換乘)
    4.同一進程下不同線程間數據很易共享(A車廂換到B車廂很容易)
    5.進程要比線程消耗更多的計算機資源(采用多列火車相比多個車廂更耗資源)
    6.進程間不會相互影響,一個線程掛掉將導致整個進程掛掉(一列火車不會影響到另外一列火車,但是如果一列火車上中間的一節車廂着火了,將影響到所有車廂)
    7.進程可以拓展到多機,進程最多適合多核(不同火車可以開在多個軌道上,同一火車的車廂不能在行進的不同的軌道上)
    8.進程使用的內存地址可以上鎖,即一個線程使用某些共享內存時,其他線程必須等它結束,才能使用這一塊內存。(比如火車上的洗手間)-"互斥鎖"進程使用的內存地址可以限定使用量(比如火車上的餐廳,最多只允許多少人進入,如果滿了需要在門口等,等有人出來了才能進去)-“信號量”
    '''
    

14.GIL

  • 背景:

    1.GIL是全局解釋器鎖,用來保證數據安全。

    2.cpu在同一時間只能執行一個線程。(單核cpu的多線程其實是單線程的並發,並行是同一時刻干多個事情,並發是相同的時間間隔干多個事情)

  • GIL的執行過程

    在python中,GIL的執行過程是:1.線程獲取GIL。2.執行代碼直至sleep或者python虛擬機將其掛起。3.釋放GIL

    從這里看出,某個線程想要執行,必須先拿到GIL,我們可以把GIL看作是“通行證”,並且在一個python進程中,GIL只有一個。拿不到通行證的線程,就不允許進入CPU執行,因此python的多線程其實是單線程

  • python多線程的效率如何呢?

    判斷效率前,首先需要明確GIL鎖的釋放方法:當前線程遇見IO操作或者ticks計數達到100時,自動釋放GIL。(在python3中改為了計時器,達到一定的時間來釋放)

    • 對於計算密集型任務來說,ticks計數很容易達到100,這樣很容易出發GIL的釋放於再競爭,容易造成資源浪費。
    • 對於IO密集型任務來說,IO操作會進行IO等待。此時,開啟多線程能在線程A等待時,自動切換到線程B,可以不浪費CPU的資源,從而能提升程序執行效率
    • 需要強調的是,多核多線程比單核單線程更差,因為涉及了核與核之間線程的競爭,容易造成線程顛簸。
  • 綜上所述,在多核任務下,每個進程都有一個GIL鎖,因此多進程類似於多線程,多進程的執行效率更高。

參考:https://zhuanlan.zhihu.com/p/20953544

15.內置數據類型、可變數據類型、不可變數據類型

  • 內置數據類型

    數字,字符串,列表,字典,元組,集合

  • 可變數據類型

    列表,集合,字典

  • 不可變數據類型

    數字,字符串,元組

16.python的特點

python是面向對象的解釋型的腳本語言,主要特點如下

  • 簡單好學(近乎偽代碼)
  • 開源
  • 可擴展性(第三方庫很多)
  • 可移植性
  • 解釋型(編譯型語言:c++/c需要編譯器編譯,python之間利用解釋器,將源代碼轉換成機器語言)
  • 高級語言(自動管理內存,封裝了很多實現細節)
  • 功能強大

17.python解釋器的種類與特點

  • CPython

    c語言開發的 使用最廣的解釋器

  • IPython

    • 基於cpython之上的一個交互式計時器 交互方式增強 功能和cpython一樣
  • JPython

    • 運行在Java上的解釋器 直接把python代碼編譯成Java字節碼執行


免責聲明!

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



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