Python高級用法 #GIL #拷貝 #私有化 #MRO #類 #實例對象 #with與上下文管理器

一、全局解釋器鎖GIL

  • GIL全局解釋器鎖

1.1查看資源佔用

  • 輸入htop

在這裏插入圖片描述

1.2單線程死循環

請用虛擬機運行

while True:
    pass
  • 會發現一個核佔滿了
  • 再在另一個終端裏,運行同一個文件,會發現。兩個核都佔滿了。

1.3多線程死循環

請用虛擬機運行

import threading

# 子線程死循環
def test():
    while True:
        pass


t1 = threading.Thread(target=test)
t1.start()

# 主線程死循環
while True:
    pass
  • 會發現兩個核都沒滿,但是兩個核的佔用率之和爲100%

1.4多進程死循環

請用虛擬機運行

import multiprocessing

# 子進程死循環
def test():
    while True:
        pass


p1 = multiprocessing.Process(target=test)
p1.start()

# 主進程死循環
while True:
    pass
  • 會發現兩個核佔滿了

1.5GIL全局解釋器鎖

  • 爲什麼多線程不會真的併發?答,其實只有一個線程在執行,一個線程執行時,另一個在休息。
  • GIL讓多線程程序,保證同一時刻只有一個線程在運行。運行多線程時,線程之間先搶鎖,再運行。
  • 官網推薦的解釋器爲CPython解釋器,由C語言寫成。由於歷史原因,CPython裏有GIL。
  • 多線程爲什麼比單線程快?答,因爲網速和阻塞。

  • 計算密集型(沒有延時和阻塞) : 用多進程
  • io密集型(輸入輸出密集型/讀寫/收發):用多線程、多協程

1.6解決GIL

  • 方法1: 換成Jpython等其他解釋器運行
  • 方法2: 換成其他語言實現
  • 以C語言爲例,實現:

在這裏插入圖片描述

  • 第一行,如果需要在屏幕上輸出的話,就必須要這一行
  • C語言必須要有一個main函數
    編譯剛纔的文件:gcc 文件名
    得到一個`a.out’,運行它
    在這裏插入圖片描述

  1. 準備一個loop.c文件(編寫死循環代碼)
    在這裏插入圖片描述
  2. 把loop.c編譯成一個動態庫(linux平臺下)
    在這裏插入圖片描述
  3. 準備一個test.py文件
    在這裏插入圖片描述

二、拷貝

1.深拷貝與淺拷貝

  • 淺拷貝: 只拷貝最上面這層
  • 深拷貝: 拷貝整個數據
  • Python中一般是新建指向。其它語言(C語言),一般是拷貝數據。
  • id()可以知道數據所在的地址
  • Python中拷貝:
import copy

a = 1
# 淺拷貝
b = copy.copy(a)

# 深拷貝
c = copy.deepcopy(a)
  • 當一個變量 = XXX時,約定爲:這個變量指向XXX
  • 原理圖:
    在這裏插入圖片描述
  • 說明(淺拷貝): 淺拷貝只拷貝最上層。你讓我拷誰,我拷誰。把要拷貝的原封不動拷貝下來,管你裏面是數據還是引用,複製就完了。
  • 說明(深拷貝): 深拷貝拷貝到了最下層。我會把最深層的數據挖出來複製一遍。
  • 淺拷貝:若最上層是不可變類型 (元組),只會建立指向。
  • 深拷貝:若所有層都是不可變類型 (元組),同樣只會建立指向。只要有一層是可變類型 ,就會真正地深拷貝。

2.拷貝的其它方式

2.1切片是淺拷貝

  • b = copy.copy(a) 等同於 b = a[:]

2.2複製字典是淺拷貝

  • 字典裏存的是引用(值在別的地方存着)

2.3傳遞參數傳遞的是引用

  • 傳遞參數傳遞的是引用
  • 如果不希望對原參數進行修改,那麼:
    function(copy.deepcopy(參數))

三、私有化

變量名 說明
XX 公有變量
_XX 私有化屬性或方法。禁止導入。類對象和子類可以訪問。
__XX 避免與子類屬性命名衝突,無法在外部直接訪問(名字重整所以訪問不到)
__XX__ 魔法對象和屬性
XX_ 用於避免與Python關鍵字衝突

四、import

1.1導入模塊的幾種方式

  • import 有兩個功能。一、讓python解釋器去加載模塊。二、定義一個變量,使它指向這個模塊
導入模塊
import a
import a, b
import a as A
from a import aaa
from a import aaa, bbb
from a import *

1.2導入模塊的先後順序

  • 按下面列表的先後順序
    在這裏插入圖片描述
  • 添加模塊的搜索路徑

sys.path.append(“一個模塊的路徑”)

  • 搜索模塊插隊

sys.path.insert(0, “一個模塊的路徑”)

1.3重新加載模塊

  • 若先導入模塊且程序未結束時,修改了這個模塊的代碼。程序不會改變,因爲原先的模塊已經被加載到緩存中。再次使用import程序導入,也不會改變(import可以防止模塊重複導入)。
  • 重新加載模塊:
from imp import reload

reload(希望重新加載的模塊名)
  • 注意: 只有import形式可以,不能用from…import形式
  • 注意: 重新加載之前,一定要先導入這個模塊

1.4from的問題

  • 假如data.py文件中有一個變量a = 1,有兩種方式導入
  • 方式1: import data
  • 方式2: from data import a
# 方式1,會改變data裏的變量值
import data
data.a = 2  # 此時,data模塊裏的a值會變爲2

from data import a
a = 2  # 此時,data裏的a值不會變
  • 原來,from … import … ,相當於拿了一個變量名建立了對1的引用(而不是對data.py裏a的引用)。a=2就是把a對1的引用,變爲對2的引用。所以不會改變原模塊的值。
  • 而import data,相當於拿一個變量名建立了對模塊本身的引用,data.a就通過前面的data. 來找到這個模塊下的a, 對其進行修改。

五、封裝、繼承、多態

1.封裝

  • python3中每一個對象都有一個屬性__class__,通過這個屬性,就知道誰創建了這個對象(即它的模板),從而找到了類方法
  • __dict__可以標記一個對象中,究竟有些什麼屬性。
    在這裏插入圖片描述
  • 爲什麼沒有num,卻有num2?答,因爲num2是這個實例獨有的屬性。
  • 方法裏的self能不能改成其它的?答,能,它只是個形參,但最好遵循規定。

2.繼承

  • 繼承可以解決冗餘
  • 繼承可以進行迭代

3.多態

  • 如果沒有重寫父類方法,則會調用父類方法。如果重寫,則調用子類方法。這種調用,但不清楚是調用子類還是父類的方法,就叫多態。

六、繼承中MRO

1.重寫和重載

  • python中子類寫與父類同名的方法,叫做重寫(就是覆蓋)
  • python中的函數名就是一個變量名,這個變量名指向了一個函數而已。(所以python中幾乎都是重寫)
  • 重載: 同樣的方法,因爲傳入參數的不同,導致的結果不同。(比如python中的數字相加和字符串相加)

2.多繼承mro

在這裏插入圖片描述

2.1 父類.方法名

  • 子類可以通過父類.方法名(self, 參數)來調用父類的方法。但是有一個問題,當孫調用子1和子2的方法時,子1和子2會調用父的方法兩次!
  • 代碼:
    在這裏插入圖片描述
  • 結果:
    在這裏插入圖片描述

2.2 super().方法名

  • 子類可以通過super().方法名(參數)來調用父類的方法。
  • __mro__可以打印出一個對象,這個對象決定調用方法的先後順序。
  • 代碼:
    在這裏插入圖片描述
  • 結果:
    在這裏插入圖片描述
  • 以孫爲例,在元組中找到孫,它的下一個是子1,所以super()調用的是子1。
  • 注意: super()裏可以傳參數(類名),不傳時,默認使用所屬類名。比如,在孫類下的super(),相當於super(孫)。

2.3 mro是什麼

  • mro是python3中利用C3算法得到的元組,是一個順序表。C3算法可以使得不會重複調用,但也導致了有時不會調用到父類方法,比如子1調用的是子2的方法,如果子2不調用父類方法,那麼父類方法就不被調用。

3.單繼承mro

七、*元組參數&**關鍵字參數

1.複習元組參數和關鍵字參數

  • 代碼:
    在這裏插入圖片描述
  • 結果:在這裏插入圖片描述

2.關鍵字被作爲元組參數傳入了

  • 代碼:
    在這裏插入圖片描述
  • 結果:
    在這裏插入圖片描述

3.拆包

  • 代碼:
    在這裏插入圖片描述
  • 結果:
    在這裏插入圖片描述
  • 原來,*和**相當於拆包,一個拆元組,一個拆字典

八、類與實例、屬性與方法

1. 類與實例的內存空間

  • 對象: 擁有自己的內存空間,和自己的屬性方法的就叫對象。
  1. __new__=====>創建對象(有個內存空間)
  2. __init__=====>對剛剛申請的空間,進行初始化

self指向創建的實例對象。

  • 類屬性只有一個(共用的都放在類對象裏)。實例對象有一個方法__class__,指向類對象。通過它來訪問類對象裏的方法和屬性。

2.通過實例對象修改類屬性

  • 瞭解原理,別這樣幹
  • 實例名.類屬性 = “XXX” 並不會改變類屬性,只會在實例裏生成一個同名的實例屬性(會被優先調用)
  • 實例名.__class__.類屬性 可以改變類屬性。

3.實例方法與類方法與靜態方法

  • 實例方法:

def 實例方法名(self):

  • 類方法:

@classmethod
def 類方法名(cls):

  • 靜態方法:

@staticmethod
def 靜態方法名():

  • 兩者的區別是,實例方法默認傳入實例對象,類方法默認傳入類對象,類默認不傳參數。
  • 靜態方法類似於一個普通函數(把裝飾器抹去,取消def前縮進與類平齊)。但是這個普通函數並不想全局使用,只想這一個類、實例使用。

4.property屬性

1. property屬性含義

  • property屬性:

@property
def property屬性名(self):

  • 調用:(因爲是屬性,所以不需要括號。該方法一般用以返回一個值。)

類名.property屬性名

在這裏插入圖片描述

  • 這個可讀性更高。(希望要一個結果,如果調用方法會影響可讀性)
  • 面向對象,封裝繼承多態。這就是封裝的體現。

2. property屬性應用

  • 一般一個私有屬性__私有屬性名,會搭配一個getter公有方法,和一個setter公有方法。一個管獲取,一個管設置。
  • 原代碼:
    在這裏插入圖片描述
  • 改造後代碼:
    在這裏插入圖片描述
  • 使用裝飾器後代碼:
    在這裏插入圖片描述

九、魔法屬性

1. 私有屬性

  • 爲什麼外部獲取不了私有屬性?答,名字重整。
  • __私有屬性名被改爲了_類名__私有屬性名
  • 如果在外部使用實例名.__私有屬性名 = 一個值,並不會改變私有屬性的值,只會給實例添加一個實例屬性。
  • 實例名.__dict__可以看到一個實例的屬性,包括所屬類的私有屬性
  • 類名.__dict__可以看到一個類的所有屬性,包括魔法屬性

2.魔法屬性、方法

  • 魔法方法、魔法屬性: 當執行魔法方法時,對應Python解釋器裏的特殊操作

2.1 常用魔法屬性

  • __doc__:展示類的描述信息
  • __class__:創建這個實例對象的類
  • __module__:創建這個實例對象的類,在哪一個模塊裏
  • __init__:初始化方法
  • __new__:創建方法。與初始化方法合稱構造方法。
  • __del__:對象內存被釋放時,自動觸發
  • __call__:實例()觸發
  • __dict__:檢查類或實例對象裏的所有屬性
  • __str__:直接輸出該對象時,該對象的返回值
    在這裏插入圖片描述
    在這裏插入圖片描述

2.2 索引魔法屬性

  • 用於索引操作,當字典用。
  • __getitem__:獲取數據
  • __setitem__:設置數據
  • __delitem__:刪除數據
    在這裏插入圖片描述
    在這裏插入圖片描述

2.3 分片魔法屬性

  • 首先,來重新認識一下切片操作
    在這裏插入圖片描述
  • 用於分片操作,如:列表
    在這裏插入圖片描述

十、with與上下文管理器

1. with

  • 文件描述符fd在一個進程裏最多1024個。
def fun():
    f = open("文件名", "w")
    f.write("hello python")
    f.close()
  • 如果進程出現問題,導致close無法被正常調用,資源就會一直佔用
def fun():
    f = open("文件名", "w")
    try:
        f.write("hello python")
    except IOError:
        pass
    finally:
        f.close()
  • 上面的代碼,等同於:
def fun():
    with open("文件名", "w") as f:
        f.write("hello python")

2. 上下文管理器contextmanager

  • 點擊APP,進入新界面時,會把之前頁面的信息存儲,以便可以返回。以這個新界面爲基礎,再點擊新界面就叫下文,返回就叫上文。
  • 看書,一句話的前面就叫上文,後面就叫下文。
  • 任何實現了__enter__()和__exit__()方法的對象都可稱爲上下文管理器。上下文管理器可以使用with關鍵字。(文件對象實現了上下文管理器)
  • with語句後面要跟上下文管理器對象。
    在這裏插入圖片描述
  1. File(“test.txt”, “w”)會創建一個實例對象
  2. with會檢查整改實例對象是否是一個上下文管理器(檢查有沒有__enter__()和__exit__()
  3. 如果是,with會自動地調用__enter__()方法,然後把它的返回值給as後的f
  4. 假如產生了異常,就會調用__exit__()方法

3. 利用裝飾器實現上下文管理器

  • 利用yield分割。yield前的就是__enter__(),後的就是__exit__()
    在這裏插入圖片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章