欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python中append淺拷貝機(jī)制詳解

 更新時(shí)間:2023年02月07日 10:26:12   作者:程序猿-張益達(dá)  
在 Python 中,對(duì)象賦值實(shí)際上是對(duì)象的引用。當(dāng)創(chuàng)建一個(gè)對(duì)象,然后把它賦給另一個(gè)變量的時(shí)候,Python 并沒(méi)有拷貝這個(gè)對(duì)象,而只是拷貝了這個(gè)對(duì)象的引用,我們稱之為淺拷貝,這篇文章主要介紹了Python中append淺拷貝機(jī)制,需要的朋友可以參考下

Python中append淺拷貝機(jī)制

關(guān)于深淺拷貝,最直觀的理解就是:

  • 深拷貝:拷貝的程度深,自己新開(kāi)辟了一塊內(nèi)存,將被拷貝內(nèi)容全部拷貝過(guò)來(lái)了;
  • 淺拷貝:拷貝的程度淺,只拷貝原數(shù)據(jù)的首地址,然后通過(guò)原數(shù)據(jù)的首地址,去獲取內(nèi)容。

這兩者的優(yōu)缺點(diǎn)對(duì)比:

  • 深拷貝拷貝程度高,將原數(shù)據(jù)復(fù)制到新的內(nèi)存空間中。改變拷貝后的內(nèi)容不影響原數(shù)據(jù)內(nèi)容。但是深拷貝耗時(shí)長(zhǎng),且占用內(nèi)存空間。
  • 淺拷貝拷貝程度低,只復(fù)制原數(shù)據(jù)的地址。其實(shí)是將副本的地址指向原數(shù)據(jù)地址。修改副本內(nèi)容,是通過(guò)當(dāng)前地址指向原數(shù)據(jù)地址,去修改。所以修改副本內(nèi)容會(huì)影響到原數(shù)據(jù)內(nèi)容。但是淺拷貝耗時(shí)短,占用內(nèi)存空間少。

Python內(nèi)存引用

在C語(yǔ)言中,在聲明變量的時(shí)候,int a,int b,這兩條語(yǔ)句為a,b兩個(gè)變量分別賦予了兩塊不同的內(nèi)存空間,然后賦值的時(shí)候再將相應(yīng)的值存儲(chǔ)到對(duì)應(yīng)的存儲(chǔ)空間。但是在Python中變量的賦值與C語(yǔ)言是截然不同的,考慮下面的代碼:

>>> a = 2
>>> b = 2
>>> id(a)
140736259334576
>>> id(b)
140736259334576

id函數(shù)用于獲取對(duì)象的內(nèi)存地址,可以發(fā)現(xiàn),變量a和變量b的內(nèi)存地址竟然一樣!

在Python中,先生成對(duì)象,變量再對(duì)對(duì)象進(jìn)行引用,在這個(gè)例子中,1就是對(duì)象,然后a再對(duì)1進(jìn)行引用,由于常數(shù)是不可變類(lèi)型,所以1的內(nèi)存空間是一樣的,所以a,b引用的是用一塊內(nèi)存空間。雖然變量名不一樣,但是他們引用的對(duì)象是相同的。

當(dāng)然上面舉的例子是int類(lèi)型的,這屬于不可變類(lèi)型。如果是list或者dict呢?來(lái)看看下面的例子:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> id(a)
3145735383560
>>> id(b)
3145735414984

內(nèi)存地址不一致!基于此,我們步入今天的主題,來(lái)看看append方法淺拷貝機(jī)制,到底有什么坑!

append方法淺拷貝機(jī)制

Python中的append方法是一個(gè)常用的方法,可以將一個(gè)對(duì)象添加到列表末尾。相信大家一定都用過(guò)吧?有人去深挖這個(gè)函數(shù)的用法嗎?這里面可以存在一個(gè)大坑!

我們來(lái)看一個(gè)例子:

>>> a = [1, 3, 5, "a"]
>>> b = []
>>> b.append(a)
>>> b
[[1, 3, 5, 'a']]
>>> a.append("aha")
>>> b    # surprise?
[[1, 3, 5, 'a', 'aha']]

思考一下,明明在第三行之后并沒(méi)有對(duì)b操作,那么為什么b會(huì)發(fā)生改變呢?

回到今天的主題,事實(shí)上,append方法是淺拷貝。在Python中,對(duì)象賦值實(shí)際上是對(duì)象的引用,當(dāng)創(chuàng)建一個(gè)對(duì)象,然后把它賦值給另一個(gè)變量的時(shí)候,Python并沒(méi)有拷貝這個(gè)對(duì)象,而只是拷貝了這個(gè)對(duì)象的引用,這就是淺拷貝。

我們逐步來(lái)看。首先,b.append(a)就是對(duì)a進(jìn)行了淺拷貝,結(jié)果為b=[[1, 3, 5, 'a']],但b[0]與a引用的對(duì)象是相同的,這可以通過(guò)id函數(shù)進(jìn)行驗(yàn)證:

>>> id(b[0])
3145735177480
>>> id(a)
3145735177480

可見(jiàn),b[0]與a指向同個(gè)內(nèi)存地址。

下一步,代碼執(zhí)行a.append(0),列表是可變類(lèi)型,這一步在原地址的列表的末尾添加0,原地址的內(nèi)容被改變了但是地址沒(méi)有變(可以將Python中的list理解為鏈表,所以這個(gè)list的地址不會(huì)變,這相當(dāng)于鏈表的頭結(jié)點(diǎn)),所以a和b[0]的內(nèi)容同時(shí)被改變了,這就是為什么對(duì)a進(jìn)行append操作b會(huì)跟著發(fā)生改變。

發(fā)生這些的前提是對(duì)同一個(gè)地址上的內(nèi)容進(jìn)行操作,所以影響了指向該地址的所有變量。

所以,在日常使用append函數(shù)的時(shí)候,就需要將淺拷貝變?yōu)樯羁截?,有兩個(gè)解決方案:

  • b.append(list(a))
  • b.append(a[:])

還是上面的例子,來(lái)看看這兩個(gè)方法的結(jié)果是不是真的解決了append淺拷貝問(wèn)題。

>>> a = [1, 3, 5, "a"]
>>> b = []
>>> b.append(list(a))
>>> b
[[1, 3, 5, 'a']]
>>> a.append(0)
>>> a
[1, 3, 5, 'a', 0]
>>> b
[[1, 3, 5, 'a']]
>>> a = [1, 3, 5, "a"]
>>> b = []
>>> b.append(a[:])
>>> b
[[1, 3, 5, 'a']]
>>> a.append(10)
>>> a
[1, 3, 5, 'a', 10]
>>> b
[[1, 3, 5, 'a']]

怎么樣,問(wèn)題是不是解決了!所以日常使用中,一定要避免淺拷貝帶來(lái)的問(wèn)題!

這個(gè)append的坑,也是我在刷leetcode:77. 組合時(shí)注意到的,題解為:

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        def traversal(n, k, start_index):
            if len(path) == k:
                result.append(path[:])   # 精華在這,要解決這里的淺拷貝問(wèn)題!
                return
            
            for i in range(start_index, n + 1):
                path.append(i)
                traversal(n, k, i + 1)
                path.pop()
        
        path = []
        result = []
        traversal(n, k, 1)
        return result

如果不處理第5行處的淺拷貝問(wèn)題,會(huì)導(dǎo)致運(yùn)行處下面的結(jié)果:

為啥?因?yàn)榛厮菅剑谏厦娲a的第11行處,一直在向上回溯,所以結(jié)果運(yùn)行出來(lái)就變成了空列表!

所以,在刷回溯的題的時(shí)候,如果你使用的是Python,一定要注意這一點(diǎn)了!

補(bǔ)充:Python append() 與深拷貝、淺拷貝

深淺拷貝

在 Python 中,對(duì)象賦值實(shí)際上是對(duì)象的引用。當(dāng)創(chuàng)建一個(gè)對(duì)象,然后把它賦給另一個(gè)變量的時(shí)候,Python 并沒(méi)有拷貝這個(gè)對(duì)象,而只是拷貝了這個(gè)對(duì)象的引用,我們稱之為淺拷貝。

在 Python 中,為了使當(dāng)進(jìn)行賦值操作時(shí),兩個(gè)變量互補(bǔ)影響,可以使用 copy 模塊中的 deepcopy 方法,稱之為深拷貝。

append() 函數(shù)

當(dāng) list 類(lèi)型的對(duì)象進(jìn)行 append 操作時(shí),實(shí)際上追加的是該對(duì)象的引用。

id() 函數(shù):返回對(duì)象的唯一標(biāo)識(shí),可以類(lèi)比成該對(duì)象在內(nèi)存中的地址。

>>>alist = []
>>> num = [2]
>>> alist.append( num )
>>> id( num ) == id( alist[0] )
True

如上例所示,當(dāng) num 發(fā)生變化時(shí)(前提是 id(num) 不發(fā)生變化),alist 的內(nèi)容隨之會(huì)發(fā)生變化。往往會(huì)帶來(lái)意想不到的后果,想避免這種情況,可以采用深拷貝解決:

alist.append( copy.deepcopy( num ) )

到此這篇關(guān)于Python中append淺拷貝機(jī)制的文章就介紹到這了,更多相關(guān)Python中append淺拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python教程通過(guò)公共鍵對(duì)不同字典進(jìn)行排序示例詳解

    Python教程通過(guò)公共鍵對(duì)不同字典進(jìn)行排序示例詳解

    本篇文章是Python教程基礎(chǔ)篇,通過(guò)一些示例為大家講解Python通過(guò)公共鍵對(duì)不同字典進(jìn)行排序的方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-09-09
  • Python?中的裝飾器實(shí)現(xiàn)函數(shù)的緩存(場(chǎng)景分析)

    Python?中的裝飾器實(shí)現(xiàn)函數(shù)的緩存(場(chǎng)景分析)

    Python中的裝飾器可以用于實(shí)現(xiàn)函數(shù)的緩存,其原理是在函數(shù)執(zhí)行前,首先判斷傳入的參數(shù)是否在緩存中已經(jīng)存在對(duì)應(yīng)的計(jì)算結(jié)果,這篇文章主要介紹了Python?中的裝飾器可以用于實(shí)現(xiàn)函數(shù)的緩存,需要的朋友可以參考下
    2023-02-02
  • Python使用QQ郵箱發(fā)送Email的方法實(shí)例

    Python使用QQ郵箱發(fā)送Email的方法實(shí)例

    實(shí)際開(kāi)發(fā)過(guò)程中使用到郵箱的概率很高,那么如何借助python使用qq郵箱發(fā)送郵件呢?正好最近工作遇到這個(gè)需求,所以想著把方法分享出來(lái)方便大家,所以這篇文章主要介紹了Python使用QQ郵箱發(fā)送Email的實(shí)現(xiàn)方法,需要的朋友可以參考。
    2017-02-02
  • Django項(xiàng)目創(chuàng)建的圖文教程

    Django項(xiàng)目創(chuàng)建的圖文教程

    本文主要介紹了Django項(xiàng)目創(chuàng)建的圖文教程,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • python模塊離線安裝方式

    python模塊離線安裝方式

    這篇文章主要介紹了python模塊離線安裝方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • python使用difflib實(shí)現(xiàn)自動(dòng)查重

    python使用difflib實(shí)現(xiàn)自動(dòng)查重

    Python中有許多現(xiàn)成的庫(kù)和工具,可以方便地實(shí)現(xiàn)自動(dòng)查重的功能,其中,difflib庫(kù)就是一個(gè)專門(mén)用于比較文件和字符串差異的庫(kù),下面我們就來(lái)看看如何利用difflib實(shí)現(xiàn)自動(dòng)查重吧
    2023-11-11
  • Python實(shí)現(xiàn)求笛卡爾乘積的方法

    Python實(shí)現(xiàn)求笛卡爾乘積的方法

    這篇文章主要介紹了Python實(shí)現(xiàn)求笛卡爾乘積的方法,結(jié)合實(shí)例形式分析了Python計(jì)算笛卡爾乘積的原理與實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2017-09-09
  • Python實(shí)現(xiàn)微信表情包炸群功能

    Python實(shí)現(xiàn)微信表情包炸群功能

    這篇文章主要介紹了Python實(shí)現(xiàn)微信表情包炸群功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-01-01
  • Python ORM框架SQLAlchemy學(xué)習(xí)筆記之?dāng)?shù)據(jù)添加和事務(wù)回滾介紹

    Python ORM框架SQLAlchemy學(xué)習(xí)筆記之?dāng)?shù)據(jù)添加和事務(wù)回滾介紹

    這篇文章主要介紹了Python ORM框架SQLAlchemy學(xué)習(xí)筆記之?dāng)?shù)據(jù)添加和事務(wù)回滾介紹,需要的朋友可以參考下
    2014-06-06
  • Python3與redis交互,保存的是字符串,取出來(lái)是bytes類(lèi)型問(wèn)題

    Python3與redis交互,保存的是字符串,取出來(lái)是bytes類(lèi)型問(wèn)題

    這篇文章主要介紹了Python3與redis交互,保存的是字符串,取出來(lái)是bytes類(lèi)型問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,
    2023-09-09

最新評(píng)論