深淺拷貝

昨日回顧

# 小數據池
        針對的數據類型: int, str, bool
        小數據池能夠幫咱們緩存一個對象.
        當重複使用的時候能夠快速的響應. 直接把對象返回.
        優勢: 快速拿到對象, 節省內存
        缺點: 當小數據池中的內容太多的時候. 程序響應速度是很慢的.
# is和==
        is 比較的是內存地址.
        == 比較的是內容
# 編碼轉換 encode() : 能夠幫咱們把字符串轉換成bytes類型 decode() : 把bytes類型還原回字符串類型 bytes是字節. 是python中最小的數據單元

 

今日內容

1. 基礎數據類型補充
        大多數的基本數據類型的知識.已經學完了
        join()
        "*".join("馬虎疼") # 馬*虎*疼 把傳遞進去的參數進行迭代. 獲取到的每一個元素和前面的*進行拼接. 獲得的是字符串
        split() 切割. 切割的結果是列表

        列表和字典: 都不能在循環的時候直接刪除
        (##可迭代對象在循環遍歷的時候是都不能增長元素也不能刪除元素的,列表是刪不乾淨,達不到你要的效果,字典是直接報錯.
        總之,在自身遍歷的同時不能改變自身的長度,可是是能夠在遍歷的同時對元素進行修改和查詢的,這個並不會對對象自己的長度形成改變.
        列表的改變必定要用到索引去改變元素,字典必定要使用鍵,若是是在遍歷的過程當中直接對列表的元素自己進行修改並賦值,只是改變了中間變量,
        並無塞進原來的列表中.字典同理.字典的key就至關於列表的索引)
        把要刪除的內容記錄在新列表中而後循環這個新列表. 刪除舊列表(或舊字典)

        fromkeys() ##是字典的一個方法
        坑1: 返回新字典. 不會更改老字典
        坑2: 當value是可變的數據類型. 各個key共享同一個可變的數據類型. 其中一個被改變了. 其餘都跟着變

        # 程序員找工做和菜市場大媽買白菜是同樣的


    2. 深淺拷貝(重點, 難點)
        1. = 沒有建立新對象, 只是把內存地址進行了複製
        2. 淺拷貝  lst.copy() 只拷貝第一層.(##第一層的含義是遇見內部的元素是可變的數據類型(好比list, set, dict)時仍是隻把內存地址進行拷貝)
        3. 深拷貝
            import copy
            copy.deepcopy() 會把對象內部的全部內容進行拷貝

 

一.基礎數據類型補充

lst = ["alex", "dsb", "wusir", "xsb"]
# 使用前面的字符串. 對後面的列表進行拼接,拼接的結果是一個字符串
s = "_".join(lst)
print(s)


# split() 根據你給的參數進行切割, 切割的結果是列表.##不傳入參數的話,默認是以空白(包括全部的空格,\t,\n)進行分割,長度不相同的空白也當成是相同的分隔符

s = "alex_dsb_wusir_xsb"
lst = s.split("_")  # 列表
print(lst)

# 須要把字符串轉化成列表: str.split()==>lst
# 把列表轉化成字符串: "*".join(lst) ==> str


# print("*".join("周潤發"))  # 用迭代的方式進行的拼接

# 2. 關於刪除
lst = ["籃球", "排球" ,"足球", "電子競技", "檯球"]

lst.clear()

for el in lst:
    lst.remove(el)

print(lst) # 刪不乾淨.緣由是: 刪除一個. 元素的索引要從新排列, for循環向後走一個. 差一個

for i in range(len(lst)): # 0 1 2 3 4
    lst.pop(0)
print(lst)


lst = ["籃球", "排球" ,"足球", "電子競技", "檯球"]
# 最合理的刪除方式:
# 1. 把要刪除的內容寫在新列表中.
# 2. 循環這個新列表. 刪除老列表

# 需求: 刪除列表中代球字的運動項目
new_lst = []
for el in lst:
    if "" in el:
        new_lst.append(el) # 記錄要刪除的內容

# 要刪除的列表
print(new_lst)
# 循環新列表. 刪除老列表
for el in new_lst: # ['籃球', '排球', '足球', '檯球']
    lst.remove(el)

print(lst)


# 字典
# 字典在被循環的時候是不能刪除的.
dic = {"張無忌":"乾坤大挪移", "周芷若":"", "趙敏":"賣萌"}

for k in dic:
    # dic.pop(k)  # dictionary changed size during iteration
    dic["滅絕師太"] = "倚天屠龍劍" # dictionary changed size during iteration

# 把要刪除的key保存在一個新列表中
# 循環這個列表.刪除字典中的key:value
lst = []
for k in dic:
    lst.append(k)

# 循環列表
# 刪除字典中的內容
for el in lst:
    dic.pop(el)
print(dic)


s = {"楊逍", "範瑤", "韋一笑", "謝遜"} # set集合
for el in s:
    s.remove(el) # Set changed size during iteration

# 集合和字典是一家人(##字典和集合本身自己都是可變的數據類型,而對於內部的元素都有要求)
# 字典: key必須是不可變的. 可哈希的, (內部的key是)不重複的
# 集合: 元素必須是不可變的. 可哈希的, (內部的元素是)不重複的
'''
字典:初始化時候就重複寫key的話,後面的一個會把前面一個的value覆蓋;後面添加已經存在的key的話,是對以前value的修改
集合:初始化時候就重複寫元素的話,只會保留一個;後面添加已經存在的元素的話,添加不進去)(因此有一種說法是:集合就是隻存儲了key的字典
'''

dic = {"韋一笑":"青翼蝠王", "韋一笑":"張無忌"}
dic['韋一笑'] = "應天正" # 修改 # 字典的key是惟一的不重複的,對於已經存在的key所作的'添加'是修改
print(dic)



# 坑: 大坑, 神坑
# fromkeys() 幫咱們建立字典用的
# 把第一個參數進行迭代. 拿到每一項做爲key和後面的value組合成字典
d = dict.fromkeys("張無忌", "趙敏") # 建立字典
print(d)



# 坑1: 返回新字典. 和原來的字典沒有關係
dic = {}
d = dic.fromkeys("風扇哥", "很困")
print(dic) # {}
print(d)  # {'風': '很困', '扇': '很困', '哥': '很困'}



# # 坑2: 若是value是可變的數據類型,
# # 那麼其中一個key對應的value執行的更改操做. 其餘的也跟着變
d = dict.fromkeys("胡辣湯", [])
print(d) # {'胡': [], '辣': [], '湯': []}
print(id(d['']))
print(id(d['']))
print(id(d['']))

d[''].append("河南特點")
print(d)  # {'胡': ['河南特點'], '辣': ['河南特點'], '湯': ['河南特點']}


v = dict.fromkeys(['s1', 's2'], [])
v['s1'].append(123)
print(v)  # {'s1': [123], 's2': [123]}

v['s1'] = 567
print(v)  # {'s1': 567, 's2': [123]}
View Code

 

字符串和列表互轉

#"拼接符".join(可迭代對象) 用迭代的方式進行的拼接,結果是字符串
li = ["李嘉誠」, 」麻花藤」,」黃海峯」,」劉嘉玲"]
s = "_".join(li)  # 列表轉字符串
print(s)

li =「黃花大閨女」
s = "_".join(li)  # 字符串的迭代拼接
print(s) 

# str.split("切割符") 根據你給的分割符進行切割(分割符至關於刀口,不會保留), 切割的結果是列表
s = "alex_dsb_wusir_xsb"
lst = s.split("_")  # 字符串轉列表
print(lst)

 

列表刪除元素

循環刪除列表中的每個元素html

# 使用remove
li = [11, 22, 33,44] for e in li:   li . remove(e) print(li) 結果: [22,44]

分析緣由:
for的運行過程會有一個指針來記錄當前循環的元素是哪個.一開始這個指針指向第0個,而後獲取到第0個元素,緊接着刪除第0個.這個時候原來是第一個的元素會自動的變成第0個.而後指針向後移動一次,指向1元素.這時原來的1已經變成了0,也就不會被刪除了.


用del刪除試試看:python

li = [11, 22,33,44]
for i in range(0, len(li)):  #range只在程序第一次走到 for循環的時候判斷索引範圍(此時len(li)=4),接下去就固定了,不然每次都改變的話,每次都只能是0
    del li[i]
print(li)

結果: 報錯
# i= 0, 1, 2 刪除的時候li[0] 被刪除以後. 後⾯一個就變成了第0個.
# 以此類推. 當i = 2的時候. list中只有一個元素. 可是這個時候刪除的是第2個 確定報錯啊

 

通過分析發現. 循環刪除都不⾏行行. 不管是用del仍是用remove. 都不能實現. 那麼pop呢?程序員

for el in li:
    li.pop() # pop也不⾏
print(li) 

結果: 
[11, 22] # 循環到元素11,刪除44;循環到元素22,刪除33,再往下就沒有元素能夠繼續了

只有這樣纔是能夠的:面試

for i in range(0, len(li)): # 循環len(li)次, 而後從後往前刪除 
    li.pop()
print(li)

或者. 用另外一個列表來記錄你要刪除的內容. 而後循環刪除緩存

li = [11, 22, 33, 44]
del_li = []
for e in li:
    del_li.append(e)
for e in del_li:
    li.remove(e)
print(li)

注意:因爲刪除元素會致使元素的索引改變, 因此容易出現問題. 盡量不要在循環中直接去刪除元素. 能夠把要刪除的元素添加到另外一個集合中而後再批量刪除. app

 

字典的建立和刪除元素

字典的建立

dict中的fromkey(),能夠幫咱們經過list來建立一個dictide

dic = dict.fromkeys(["jay", "JJ"], ["周杰倫", "麻花藤"]) 
print(dic)
結果: {
'jay': ['周杰倫', '麻花藤'], 'JJ': ['周杰倫', '麻花藤']}

前⾯列表中的每一項都會做爲key, 後面列表中的內容做爲value. 生成dict編碼

好了. 注意:spa

dic = dict.fromkeys(["jay", "JJ"], ["周杰倫", "麻花藤"]) 
print(dic)
dic.get(
"jay").append("胡⼤大")
print(dic)
結果: {
'jay': ['周杰倫', '麻花藤', '胡⼤大'], 'JJ': ['周杰倫', '麻花藤', '胡⼤大']}

代碼中只是更改了jay那個列表. 可是因爲jay和JJ用的是同一個列表. 因此. 前面那個改了. 後面那個也會跟着改指針

 

字典刪除元素

dic = {'k1': 'alex', 'k2': 'wusir', 's1': '⾦金金⽼老老板'} 

# 刪除key中帶有'k'的元素 for k in dic: if 'k' in k: del dic[k] # 報錯dictionary changed size during iteration, 在循環迭代的時候不容許進⾏刪除操做
print(dic)

那怎麼辦呢? 把要刪除的元素暫時先保存在一個list中, 而後循環list, 再刪除

dic = {'k1': 'alex', 'k2': 'wusir', 's1': '⾦金金⽼老老板'} 
dic_del_list = []
# 刪除key中帶有'k'的元素
for k in dic:
    if 'k' in k:
        dic_del_list.append(k)
for el in dic_del_list:
    del dic[el]
print(dic)

 

二.深淺拷貝

"""
首先注意:拷貝會建立新對象,引用不會建立新對象(一個對象一個不一樣的內存地址)

=:其實就是對象的引用(別名,二者都指向同一個對象),因此會隨着原對象的操做而徹底同步的變化

深拷貝:徹底拷貝了父對象及其子對象,二者是徹底獨立的,因此徹底不會隨着原對象的操做而發生任何的變化.

淺拷貝:拷貝父對象,不會拷貝父對象內部的子對象,因此a和b是一個獨立的對象,可是他們的子對象仍是指向統一的對象(父對象是拷貝,子對象是引用==>父對象是徹底獨立的,子對象是指向同一個對象的),因此對父對象的操做不會隨之變化,對子對象的操做(只有可變數據類型才能夠對其元素進行增刪改)會隨之變化.

一個形象的比喻:
=:把個人做業借給你拿給老師檢查
淺拷貝:你按照個人做業抄了一份給老師檢查,可是少抄了一頁,只抄了'見第二頁'
深拷貝:你按照個人做業抄了一份給老師檢查,連第二頁也抄了
"""

# =
# # 從上到下只有一個列表被建立
lst1 = ["胡辣湯", "灌湯包", "油潑面", "麻辣香鍋"]
lst2 = lst1  # 並無產生新對象. 只是一個指向(內存地址)的賦值
# lst2 = lst1[:]  # 切片,是淺拷貝
print(id(lst1))
print(id(lst2))

lst1.append("葫蘆娃")
print(lst1)
print(lst2)


# 淺拷貝 copy 內部元素都是不可變數據類型
lst1 = ["胡辣湯", "灌湯包", "油潑面", "麻辣香鍋"]
lst2 = lst1.copy()  # 拷貝, 抄做業, 能夠幫咱們建立新的對象,和原來長的如出一轍, 淺拷貝

print(id(lst1))
print(id(lst2))

lst1.append("葫蘆娃")
print(lst1)
print(lst2)


# 淺拷貝 copy 內部元素存在可變數據類型
lst1 = ["胡辣湯", "灌湯包", "油潑面", "麻辣香鍋", ["長白山", "白洋澱", "黃鶴樓"]]
lst2 = lst1.copy() # 淺拷貝. 只拷貝第一層內容

print(id(lst1))
print(id(lst2))

print(lst1)
print(lst2)

lst1[4].append("葫蘆娃")
print(lst1)
print(lst2)


# 深拷貝 deepcopy 無論內部元素是不是可變數據類型,把元素內部的元素徹底進⾏拷⻉複製. 不會產⽣一個改變另⼀個跟着 改變的問題
# 引入一個模塊
import copy

lst1 = ["胡辣湯", "灌湯包", "油潑面", "麻辣香鍋", ["長白山", "白洋澱", "黃鶴樓"]]
lst2 = copy.deepcopy(lst1) # 深拷貝: 對象內部的全部內容都要複製一份. 深度克隆(clone). 原型模式

print(id(lst1))
print(id(lst2))

# print(lst1)
# print(lst2)
#
# lst1[4].append("葫蘆娃")
# print(lst1)
# print(lst2)


# 爲何要有深淺拷貝? 類比於作做業和抄做業,抄做業是不須要思考的,計算複製也同樣
# 提升建立對象的速度
# 計算機中最慢的. 就是建立對象. 須要分配內存.
# 最快的方式就是二進制流的形式進行復制. 速度最快.

# 作做業? 抄做業?
View Code

 

賦值

= 沒有建立新對象, 只是把內存地址進行了複製
lst1 = ["⾦金金⽑毛獅王", "紫衫⻰龍王", "⽩白眉鷹王", "⻘青翼蝠王"] 
lst2 = lst1
print(lst1)
print(lst2)

lst1.append("楊逍") 
print(lst1) 
print(lst2)

結果:
['⾦金金⽑毛獅王', '紫衫⻰龍王', '⽩白眉鷹王', '⻘青翼蝠王', '楊逍'] 
['⾦金金⽑毛獅王', '紫衫⻰龍王', '⽩白眉鷹王', '⻘青翼蝠王', '楊逍']


dic1 = {"id": 123, "name": "謝遜"} 
dic2 = dic1
print(dic1)
print(dic2)
dic1['name'] = "範瑤" 
print(dic1) 
print(dic2)

結果: 
{'id': 123, 'name': '謝遜'}
{'id': 123,  'name': '謝遜'}
{'id': 123,  'name': '範瑤'}
{'id': 123,  'name': '範瑤'}

對於list, set, dict來講, 直接賦值. 實際上是把內存地址交給變量. 並非複製一分內容. 因此. lst1的內存指向和lst2是同樣的. lst1改變了, lst2也發生了改變

 

 

淺拷⻉

lst1 = ["何炅", "杜海海濤","周渝⺠民"] 
lst2 = lst1.copy() 
# 或者
# import copy
# lst2 = copy.copy(lst1) # 使用 copy 模塊的 copy.copy( 淺拷貝 ) lst1.append(
"李李嘉誠") print(lst1) print(lst2) print(id(lst1), id(lst2)) 結果: 兩個lst徹底不同. 內存地址和內容也不同. 發現實現了內存的拷⻉ lst1 = ["何炅", "杜海海濤","周渝⺠民", ["麻花藤", "⻢馬芸", "周筆暢"]] lst2 = lst1.copy() lst1[3].append("⽆無敵是多磨寂寞") print(lst1) print(lst2) print(id(lst1[3]), id(lst2[3])) 結果: ['何炅', '杜海海濤', '周渝⺠民', ['麻花藤', '⻢馬芸', '周筆暢', '⽆無敵是多磨寂寞']] ['何炅', '杜海海濤', '周渝⺠民', ['麻花藤', '⻢馬芸', '周筆暢', '⽆無敵是多磨寂寞']] 4417248328 4417248328

淺拷⻉. 只會拷貝第一層. 第二層的內容不會拷貝. 因此被稱爲淺拷⻉

 

深拷貝

import copy
lst1 = ["何炅", "杜海海濤","周渝⺠民", ["麻花藤", "⻢馬芸", "周筆暢"]] 
lst2 = copy.deepcopy(lst1)
lst1[3].append("⽆無敵是多磨寂寞")
print(lst1)
print(lst2)
print(id(lst1[3]), id(lst2[3]))
結果:
['何炅', '杜海海濤', '周渝⺠民', ['麻花藤', '⻢馬芸', '周筆暢', '⽆無敵是多磨寂寞']] 
['何炅', '杜海海濤', '周渝⺠民', ['麻花藤', '⻢馬芸', '周筆暢']]
4447221448 4447233800

深度拷⻉. 把元素內部的元素徹底進⾏拷貝複製. 不會產⽣生一個改變另外一個跟着改變的問題

 

 

注意:

針對可變類型而言,淺拷貝只是拷貝"第一層",深拷貝就是拷貝全部層級中的可變類型.

不可變的數據類型深淺拷貝是同樣的效果,都不會產生一個改變另外一個跟着改變的結果.

lst1 = ["胡辣湯", "灌湯包", "油潑面", ["麻辣香鍋"]]
lst2 = lst1  # 並無產生新對象. 只是一個指向(內存地址)的賦值
print(id(lst1))
print(id(lst2))

lst1.append("葫蘆娃")
lst1[3].append("葫蘆娃")
print(lst1)
print(lst2)

print("---------------------------------------------------------")

lst1 = ["胡辣湯", "灌湯包", "油潑面", ["麻辣香鍋"]]
lst2 = lst1[:]  # 切片,是淺拷貝,經過切片建立新對象,再賦值給lst2
print(id(lst1))
print(id(lst2))

lst1.append("葫蘆娃")
lst1[3].append("葫蘆娃")
print(lst1)
print(lst2)


"""
結果:
4361847880
4361847880
['胡辣湯', '灌湯包', '油潑面', ['麻辣香鍋', '葫蘆娃'], '葫蘆娃']
['胡辣湯', '灌湯包', '油潑面', ['麻辣香鍋', '葫蘆娃'], '葫蘆娃']
---------------------------------------------------------
4361848072
4361806280
['胡辣湯', '灌湯包', '油潑面', ['麻辣香鍋', '葫蘆娃'], '葫蘆娃']
['胡辣湯', '灌湯包', '油潑面', ['麻辣香鍋', '葫蘆娃']]

"""

 

最後咱們來看一個面試題: 

a = [1, 2]
a[1] = a
print(a[1])  # [1, [...]]

 

今日做業

今日做業及默寫

相關文章
相關標籤/搜索