記錄我的 python 學習歷程-Day06 is id == / 程式碼塊 / 集合 / 深淺拷貝
一、is == id 用法
在Python中,id是記憶體地址, 你只要建立一個數據(物件)那麼就會在記憶體中開闢一個空間,將這個資料臨時載入到記憶體中,這個空間有一個唯一標識,就好比是身份證號,標識這個空間的叫做記憶體地址,也就是這個資料(物件)的id,那麼你可以利用id()去獲取這個資料的記憶體地址:
name = 'Dylan'
print(id(name)) # 4319709032
== 是比較兩邊的數值是否相等,其反回的結果是 True 或 False。
is 是比較兩邊的記憶體地址是否相等,如果記憶體地址相等,那麼兩邊所指向是同一個記憶體地址。其反回的結果是 True 或 False。
name = ['Dylan']
name2 = ['Dylan']
print(name is name2) # False
print(name == name2) # True
print(id(name)) # 4387384328
print(id(name2)) # 4387382920
# 這裡表示:name name2數值是一樣的,但卻不是同一個記憶體地址。
所以:如果記憶體地址相同,那麼值肯定相同,但是如果值相同,記憶體地址不一定相同。
二、程式碼塊
Python 程式是由程式碼塊構造的,塊是一個 python 程式的文字,他是作為一個單元執行的。
程式碼塊:一個模組、一個函式、一個類、一個檔案等都是一個程式碼塊。
作為互動方式輸入的每個命令都是一個程式碼塊。
什麼是互動方式?
就是咱們在 cmd 中進入 Python 直譯器裡面,每一行程式碼都是一個程式碼塊。
三、同一程式碼塊下的快取機制
- 前提條件:同一個程式碼塊內。
- 機制內容:Python在執行同一個程式碼塊的初始化物件的命令時,會檢查是否其值是否已經存在,如果存在,會將其重用。換句話說:執行同一個程式碼塊時,遇到初始化物件的命令時,他會將初始化的這個變數與值儲存在一個字典中,在遇到新的變數時,會先在字典中查詢記錄,如果有同樣的記錄那麼它會重複使用這個字典中的之前的這個值。所以在你給出的例子中,檔案執行時(同一個程式碼塊)會把i1、i2兩個變數指向同一個物件,滿足快取機制則他們在記憶體中只存在一個,即:id相同。
- 適用物件:int(float)、bool、str。
- 具體細則:
- int(float):任何數字在同一程式碼塊下都會複用。
- bool:True 和 False 在字典中會以1,0的方式存在,並且複用。
- str:幾乎所有的字串都會符合快取機制。
- 優點:提升效能,節省記憶體。
不同程式碼塊下的快取機制(小資料池)
前提條件:不同程式碼塊內。
機制內容:Python自動將-5~256的整數進行了快取,當你將這些整數賦值給變數時,並不會重新建立物件,而是使用已經建立好的快取物件。
python會將一定規則的字串在字串駐留池中,建立一份,當你將這些字串賦值給變數時,並不會重新建立物件, 而是使用在字串駐留池中建立好的物件。
其實,無論是快取還是字串駐留池,都是python做的一個優化,就是將~5-256的整數,和一定規則的字串,放在一個‘池’(容器,或者字典)中,無論程式中那些變數指向這些範圍內的整數或者字串,那麼他直接在這個‘池’中引用,言外之意,就是記憶體中之建立一個。
適用物件:int(float)、bool、str。
具體細則:
- int(float):那麼大家都知道對於整數來說,小資料池的範圍是-5~256 ,如果多個變數都是指向同一個(在這個範圍內的)數字,他們在記憶體中指向的都是一個記憶體地址。
- bool:True 和 False 在字典中會以1,0的方式存在,並且複用。
- str:滿足規則的字串。
優點:提升效能,節省記憶體。
- 總結:
- 面試題考。
- 回答的時候一定要分清楚:同一個程式碼塊下適用一個快取機制。不同的程式碼塊下適用另一個快取機制(小資料池)
- 小資料池:數字的範圍是-5~256.
- 快取機制的優點:提升效能,節省記憶體。
四、集合(瞭解)
集合是無序的,不重複的資料集合,它裡面的元素是可雜湊的(不可變型別),但是集合本身是不可雜湊(所以集合做不了字典的鍵)的。
- 集合最重要的兩點:
- 去重,把一個列表變成集合,就自動去重了。
- 關係測試,測試兩組資料之前的交集、差集、並集等關係。
集合和字典
集合的建立
# 方法一 set1 = set({'name', 'Dyaln', 'age', 111, 434}) # 方法二 set1 = {'name', 'Dyaln', 'age', 111, 434}
字典和集合的格式:
# 字典 dic = {'name':'Dylan', 'age': 18} # 集合 set1 = {'name', 'age', 18, False, True, }
空字典:
dic = {} # 或者 {} print({}, type({})) # {} <class 'dict'>
空集合:
set() print(set(), type(set())) # set() <class 'set'>
集合的有效性:
set1 = {[1, 3, 5], 3, {'name': 'Dylan'}} print(set1) # 報錯 File "/Users/yaoyaoba/Full_stack_22/day06/練習.py", line 24, in <module> set() <class 'set'> set1 = {[1, 3, 5], 3, {'name': 'Dylan'}} TypeError: unhashable type: 'list' # 集合內的元素必須是 可合希型別(不可變資料型別)
集合的操作
增
set.add()
set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'} set1.add('xx') print(set1) # {'xiaowang', 'xx', 'age', 'yaoyao', 'name', 'Dylan'}
set.update() 迭代增加(有重複的會自動除去)
set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'} set1.update('abcdedfdaefdafdsa') print(set1) # {'yaoyao', 'age', 'd', 'e', 'a', 'Dylan', 'xiaowang', 'b', 'f', 'c', 'name', 's'}
刪
set.remove() 按元素刪除
set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'} set1.remove('age') print(set1) # {'xiaowang', 'yaoyao', 'Dylan', 'name'}
set.pop() 隨機刪除
set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'} set1.pop() print(set1) # {'yaoyao', 'age', 'xiaowang', 'Dylan'}
set.clear() 清空集合
set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'} set1.clear() print(set1) # set()
del set 刪除集合
set1 = {'name', 'Dylan', 'xiaowang', 'yaoyao', 'age'} del set1 print(set1) # 會報錯,因為己經沒有這個集合了 # 報錯資訊如下: Traceback (most recent call last): File "/Users/yaoyaoba/Full_stack_22/day06/練習.py", line 28, in <module> print(set1) # 會報錯,因為己經沒有這個集合了 NameError: name 'set1' is not defined
集合的其它操作
交集。(& 或者 intersection)
set1 = {1, 2, 3, 4, 5} set2 = {4, 5, 6, 7, 8} print(set1 & set2) # {4, 5}
並集。(| 或者 union)
set1 = {1, 2, 3, 4, 5} set2 = {4, 5, 6, 7, 8} print(set1 | set2) # {1, 2, 3, 4, 5, 6, 7, 8}
差集。(- 或者 difference)
set1 = {1, 2, 3, 4, 5} set2 = {4, 5, 6, 7, 8} print(set1 - set2) # {1, 2, 3}
反交集。 (^ 或者 symmetric_difference)
set1 = {1, 2, 3, 4, 5} set2 = {4, 5, 6, 7, 8} print(set1 ^ set2) # {1, 2, 3, 6, 7, 8}
子集與超集
set1 = {1,2,3} set2 = {1,2,3,4,5,6} print(set1 < set2) print(set1.issubset(set2)) # 這兩個相同,都是說明set1是set2子集。 print(set2 > set1) print(set2.issuperset(set1)) # 這兩個相同,都是說明set2是set1超集。
列表去重
l1 = [1,'Dylan', 1, 2, 2, 'Dylan',2, 6, 6, 3, 'Dylan', 4, 5] # set1 = set(l1) # l1 = list(set1) # print(l1) # 用處:資料之間的關係,列表去重。
五、深淺copy
copy其實就是複製一份,也就是所謂的抄一份。深淺copy其實就是完全複製一份,和部分複製一份的意思。
先看賦值運算
l1 = [1, 2, 3, ['Dylan', 'age']] l2 = l1 l1.append(456) print(l1) # [1, 2, 3, ['Dylan', 'age'], 456] print(l2) # [1, 2, 3, ['Dylan', 'age'], 456] print(id(l1)) # 4387382920 記憶體地址是一樣的 print(id(l2)) # 4387382920 記憶體地址是一樣的
對於賦值運算來說,l1與l2指向的是同一個記憶體地址,所以它們是完全一樣的,l1,l2指向的是同一個列表,任何一個變數對列表進行改變,剩下那個變數在使用列表之後,這個列表就是發生改變之後列表。
淺拷貝 copy
l1 = [1, 2, 3, ['Dylan', 'age']] l2 = l1.copy() print(id(l1)) # 4335892104 print(id(l2)) # 4335903304 # 這說明,通過 copy 出來的新列表,在記憶體中又開闢了一塊新的記憶體空間,兩者間不是指向的同一個列表。 # 但是,如果再做如下操作你會發現什麼? print(id(l1[-1])) # 4370607112 print(id(l2[-1])) # 4370607112 # 你會發現,咦?記憶體地址是一樣的,說明是同一個資料。
由此我們可以得知,淺拷貝其實只是拷貝了一個列表的外殼。
對於淺copy來說,只是在記憶體中重新建立了開闢了一個空間存放一個新列表,但是新列表中的元素與原列表中的元素是公用的。
這裡還有一個問題:
當改變列表中的不可變資料型別時,新列表中的內容是不會一同被更改的,因為它是可雜湊資料型別,如列表中,可變資料型別被列改或增刪改,則新列表會一同更改。
深拷貝 deepcopy
import copy l1 = [1, 2, 3, ['Dylan', 'age']] l2 = copy.deepcopy(l1) print(id(l1)) # 4343088456 print(id(l2)) # 4370618248 # 這說明,通過 copy 出來的新列表,在記憶體中又開闢了一塊新的記憶體空間,兩者間不是指向的同一個列表。 print(id(l1[-1])) # 4379005512 print(id(l2[-1])) # 4379005832 # 咦?記憶體地址不一樣了,說明不是同一個資料了。 print(id(l1[0])) # 4305226112 print(id(l2[0])) # 4305226112 # 哎我去!又一樣了,咋回事兒?
深 copy 的特性就是將可變的資料型別在記憶體中重新建立一份,而不可變的資料型別則沿用之前的。
但,同樣如淺拷貝那樣,不可變的資料型別,即便記憶體地址相同,當你改變他時,新列表也不會一同被更改,只因為他是不可變資料型別(可雜湊)。
相關面試題
l1 = [1, 2, 3, [22, 33]] l2 = l1[:] l1[-1].append(666) print(l1) # [1, 2, 3, [22, 33, 666]] print(l2) # [1, 2, 3, [22, 33, 666]] 淺copy: list dict: 巢狀的可變的資料型別是同一個。 深copy: list dict: 巢狀的可變的資料型別不是同一個 。