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

Python?對(duì)象拷貝及深淺拷貝區(qū)別的詳細(xì)教程示例

 更新時(shí)間:2023年08月09日 16:56:18   作者:追憶MHyourh  
這篇文章主要介紹了Python?對(duì)象拷貝及深淺拷貝區(qū)別的詳細(xì)教程示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

Python 對(duì)象拷貝

在本篇文章中,會(huì)先介紹 Python 中對(duì)象的基礎(chǔ)概念,之后會(huì)提到對(duì)象的深淺拷貝以及區(qū)別。在閱讀后,應(yīng)該掌握如下的內(nèi)容:

  • 理解變量、引用和對(duì)象的關(guān)系
  • 理解 Python 對(duì)象中 identity,type 和 value 的概念
  • 什么是 mutable 和 immutable 對(duì)象?以及它們和 hashable 的關(guān)系
  • 深淺拷貝的過程以及區(qū)別

1.變量,引用和對(duì)象

變量無類型,它的作用僅僅在某個(gè)時(shí)候引用了特定的對(duì)象而已,具體在內(nèi)存中就是一個(gè)指針,僅僅擁有指向?qū)ο蟮目臻g大小。

變量和對(duì)象的關(guān)系在于引用,變量引用對(duì)象后,也就對(duì)應(yīng)了賦值的過程。

在 python 中一切皆為對(duì)象,具體在內(nèi)存中表示一塊內(nèi)存空間,每一個(gè)對(duì)象都會(huì)具有 identity,type 和 value 這三個(gè)內(nèi)容。

Identity, 一旦對(duì)象被創(chuàng)建后,Identity 的值便不會(huì)發(fā)生改變。在 Cpython 中,其值體現(xiàn)為內(nèi)存中保存對(duì)象的地址。is 操作符,比較對(duì)象是否相等就是通過這個(gè)值。通過 id() 函數(shù)查看它的整數(shù)形式。

Type, 和 Identity 一樣,在對(duì)象創(chuàng)建后,Type 也不會(huì)發(fā)生變化。它主要定義了一些可能支持的值和操作(如對(duì)列表來說,會(huì)有求長(zhǎng)度的操作)。通過 type() 函數(shù)可以得到對(duì)象的類型。

Value,用于表示的某些對(duì)象的值。當(dāng)對(duì)象在創(chuàng)建后值可以改變稱為 mutable,否則的話被稱為 immutable.

舉個(gè)例子,比如在 C 中,int x = 4 在內(nèi)存中,是先分配了一個(gè) int 類型的內(nèi)存空間,然后把 4 放進(jìn)空間內(nèi)。

而 Python 中,x = 4 正好相反,是為 4 分配了一塊的內(nèi)存空間,然后用 x 指向它。由于變量可以指向各種類型的對(duì)象,因此不需要像 C 一樣聲明變量。這也就是 Python 被稱為動(dòng)態(tài)類型的意義。

并且在 Python 中,變量可以刪除,但對(duì)象是無法刪除的。

2.immutable 和 mutable 對(duì)象

immutable 對(duì)象擁有一個(gè)固定的值,包括 numbers, strings, tuples. 一個(gè)新的值被保存時(shí),一個(gè)新的對(duì)象就會(huì)被創(chuàng)建。這些對(duì)象在作為常量的 hash 值中有著非常重要的作用,如作為字典的 key 時(shí)。

mutable 對(duì)象可以改變自身的值,但 id() 并不會(huì)發(fā)生改變。

當(dāng)一些對(duì)象包含對(duì)其他對(duì)象的一些引用時(shí),我們稱這些對(duì)象為 containers, 例如 list, tuple, dictionary 這些都是 containers. 這里需要注意的是,一個(gè) immutable containers 可以包含對(duì) mutable 對(duì)象的引用(如在 tuple 中包含一個(gè) list)。 但這個(gè)對(duì)象仍然稱為 immutable 對(duì)象,因?yàn)?Identity 是不變的。

3.hashable 對(duì)象

當(dāng)一個(gè)對(duì)象在生命周期內(nèi)(實(shí)現(xiàn)了 __hash__() 方法)hash 值不會(huì)發(fā)生改變,并可以與其他對(duì)象進(jìn)行比較(實(shí)現(xiàn)了 __eq__() 方法),稱之為hashable 對(duì)象。

在 Python 內(nèi)置的 immutable 對(duì)象 大多數(shù)都是 hashable 對(duì)象。immutable containers(tuples, frozenset)在引用的對(duì)象都是 hashable 對(duì)象時(shí),才是hashable 對(duì)象。mutable containers 容器都不是 hashable 對(duì)象。用戶自定義的類都是 hashable 對(duì)象,

4.淺拷貝與深拷貝

在介紹對(duì)象的拷貝前,先介紹一下 Python 中的賦值操作,可以讓我們更好的了解拷貝的過程。

5.賦值操作

賦值操作的右邊是簡(jiǎn)單表達(dá)式:

def normal_operation():
    # immutable objects
    # int
    a = 10
    b = 10
    print('----- int')
    print("id of a:{} , id of b: {}".format(id(a), id(b)))
    # id of a:1777364320 , id of b: 1777364320
    print(a == b)  # True
    print(a is b)  # True
    # str
    str_a = '123'
    str_b = '123'
    print('----- str')
    print("id of a:{} , id of b: {}".format(id(str_a), id(str_b)))
    # id of a:1615046978224 , id of b: 1615046978224
    print(str_a == str_b)  # True
    print(str_a is str_b)  # True
    # tuple
    tuple_a = (1, 2, 3)
    tuple_b = (1, 2, 3)
    print('----- tuple')
    print("id of a:{} , id of b: {}".format(id(tuple_a), id(tuple_b)))
    # id of a:1615047009696 , id of b: 1615047024856
    print(tuple_a == tuple_b)  # True
    print(tuple_a is tuple_b)  # False
    # mutable
    # set
    set_a = {1, 2, 3}
    set_b = {1, 2, 3}
    print('----- set')
    print("id of a:{} , id of b: {}".format(id(set_a), id(set_b)))
    # id of a:1615045625000 , id of b: 1615047012872
    print(set_a == set_b)  # True
    print(set_a is set_b)  # False
    # list
    list_a = [1, 2, 3]
    list_b = [1, 2, 3]
    print('----- list')
    print("id of a:{} , id of b: {}".format(id(list_a), id(list_b)))
    # id of a:1615047017800 , id of b: 1615045537352
    print(list_a == list_b)  # True
    print(list_a is list_b)  # False
    # dict
    dict_a = {"name": "xxx", "age": "123"}
    dict_b = {"name": "xxx", "age": "123"}
    print('----- dict')
    print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b)))
    # id of a:1615045521696 , id of b: 1615045522128
    print(dict_a == dict_b)  # True
    print(dict_a is dict_b)  # False

在 Cpython 中,id() 反映了對(duì)象在內(nèi)存中的地址。可以看到,對(duì)于 immutable 對(duì)象中的 number 和 string 來說,CPython 本身對(duì)其做了一定的優(yōu)化,在創(chuàng)建相同的內(nèi)容時(shí),使其 指向了相同的內(nèi)存地址,從而被復(fù)用。

但是,Python 不會(huì)對(duì)所有 mutable 對(duì)象執(zhí)行此操作,因?yàn)閷?shí)現(xiàn)此功能需要一定的運(yùn)行時(shí)成本。對(duì)于在內(nèi)存中的對(duì)象來說,必須首先在內(nèi)存中搜索對(duì)象(搜索意味著時(shí)間)。對(duì)于 number 和 string 來說,搜索到它們很容易,所以才對(duì)其做了這樣的優(yōu)化。

對(duì)于其他類型的對(duì)象,雖然創(chuàng)建的內(nèi)容相同,但都在內(nèi)存中完全創(chuàng)建了一塊新的區(qū)域。

6. 賦值操作的右邊是 Python 中已存在的變量:

def assignment_operation():
    # immutable objects
    # int
    a = 10
    b = a
    print('----- int')
    print("id of a:{} , id of b: {}".format(id(a), id(b)))
    # id of a:1777364320 , id of b: 1777364320
    print(a == b)  # True
    print(a is b)  # True
    # str
    str_a = '123'
    str_b = str_a
    print('----- str')
    print("id of a:{} , id of b: {}".format(id(str_a), id(str_b)))
    # id of a:2676110142128 , id of b: 2676110142128
    print(str_a == str_b)  # True
    print(str_a is str_b)  # True
    # tuple
    tuple_a = (1, 2, 3)
    tuple_b = tuple_a
    print('----- tuple')
    print("id of a:{} , id of b: {}".format(id(tuple_a), id(tuple_b)))
    # id of a:2676110191640 , id of b: 2676110191640
    print(tuple_a == tuple_b)  # True
    print(tuple_a is tuple_b)  # True
    # mutable
    # set
    set_a = {1, 2, 3}
    set_b = set_a
    print('----- set')
    print("id of a:{} , id of b: {}".format(id(set_a), id(set_b)))
    # id of a:2676108788904 , id of b: 2676108788904
    print(set_a == set_b)  # True
    print(set_a is set_b)  # True
    # list
    list_a = [1, 2, 3]
    list_b = list_a
    print('----- list')
    print("id of a:{} , id of b: {}".format(id(list_a), id(list_b)))
    # id of a:2676110181704 , id of b: 2676110181704
    print(list_a == list_b)  # True
    print(list_a is list_b)  # True
    # dict
    dict_a = {"name": "xxx", "age": "123"}
    dict_b = dict_a
    print('----- dict')
    print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b)))
    # id of a:2676079063328 , id of b: 2676079063328
    print(dict_a == dict_b)  # True
    print(dict_a is dict_b)  # True

而當(dāng)賦值操作的右邊是已經(jīng)存在的 Python 對(duì)象時(shí),不論是什么類型的對(duì)象,都沒有在內(nèi)存中創(chuàng)建新的內(nèi)容,僅僅是聲明了一個(gè)新的變量指向之前內(nèi)存中已經(jīng)創(chuàng)建的對(duì)象,就像提供了一個(gè)別名一樣。

>>> dict_a = {'1':1}
>>> dict_b = dict_a
>>> print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b)))
id of a:140355639151936 , id of b: 140355639151936
>>> dict_b = {}
>>> print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b)))
id of a:140355639151936 , id of b: 140355639922176

由于 dict_b = dict_a操作,讓兩個(gè)變量同時(shí)指向了同一塊內(nèi)存區(qū)域。自然 id 相等。
當(dāng)對(duì) dict_b 重新賦值時(shí),僅讓 b 指向了另外一塊內(nèi)存區(qū)域,并不會(huì)影響 a 的指向,由于兩塊內(nèi)存區(qū)域不同,自然id 并不想等。

7.改變賦值后的對(duì)象

def assignment_operation_change():
    # immutable objects
    # int
    a = 10
    print("id of a:{}".format(id(a)))  
    # id of a:1994633728
    b = a 
    a = a + 10
    print('----- int')
    print("id of a:{} , id of b: {}".format(id(a), id(b)))
    # id of a:1994634048 , id of b: 1994633728
    print(a == b)  # False
    print(a is b)  # False
    # mutable objects
    # list
    list_a = [1, 2, 3]
    list_b = list_a
    list_a.append(4)
    print('----- list')
    print("id of a:{} , id of b: {}".format(id(list_a), id(list_b)))
    # id of a:2676110181704 , id of b: 2676110181704
    print(list_a == list_b)  # True
    print(list_a is list_b)  # True

當(dāng)修改 imutable 對(duì)象時(shí),由于其本身不可改變,只能在內(nèi)存中新申請(qǐng)一塊新的空間,用于存儲(chǔ)修改后的內(nèi)容。對(duì)應(yīng)上面 a=20 的操作,這時(shí)再判斷 a 和 b 時(shí),由于指向了內(nèi)存的不同位置,所以 a,b不在相等。a 原來指向的內(nèi)存區(qū)域不會(huì)被回收,因?yàn)楝F(xiàn)在由 b 指向??梢钥吹?b 指向的內(nèi)存地址和 a 之前的指向的內(nèi)存地址是一致的。

當(dāng)修改 mutable 對(duì)象時(shí),由于都指向相同的內(nèi)存地址,所以對(duì)變量 list_a 修改的操作,也會(huì)映射到變量 list_b。

總結(jié)一下:

  • 指向 imutable 的不同變量,當(dāng)其中一個(gè)變量被修改時(shí),其他變量不受影響,因?yàn)楸恍薷暮蟮淖兞繒?huì)指向一個(gè)新創(chuàng)建的對(duì)象。
  • 指向 mutable 對(duì)象的不同變量,當(dāng)其中一個(gè)變量修改這個(gè)對(duì)象時(shí),會(huì)影響到指向這個(gè)對(duì)象的所有變量。

8.淺拷貝

淺拷貝創(chuàng)建了一個(gè)對(duì)象,這個(gè)對(duì)象包含了對(duì)被拷貝元素的參考。 所以當(dāng)使用淺拷貝來復(fù)制 conainters 對(duì)象時(shí),僅僅拷貝了那些嵌套元素的引用。

def shallow_copy():
    # immutable objects
    # int
    a = 10
    b = copy(a)
    print('----- int')
    print("id of a:{} , id of b: {}".format(id(a), id(b)))
    # id of a:1777364320 , id of b: 1777364320
    print(a == b)  # True
    print(a is b)  # True
    # str
    str_a = '123'
    str_b = copy(str_a)
    print('----- str')
    print("id of a:{} , id of b: {}".format(id(str_a), id(str_b)))
    # id of a:2676110142128 , id of b: 2676110142128
    print(str_a == str_b)  # True
    print(str_a is str_b)  # True
    # tuple
    tuple_a = (1, 2, 3)
    # Three methods of shallow copy
    # tuple_b = tuple_a[:]
    # tuple_b = tuple(tuple_a)
    tuple_b = copy(tuple_a)
    print(id(tuple_b))
    print('----- tuple')
    print("id of a:{} , id of b: {}".format(id(tuple_a), id(tuple_b)))
    # id of a:2676110191640 , id of b: 2676110191640
    print(tuple_a == tuple_b)  # True
    print(tuple_a is tuple_b)  # True
    # mutable
    # set
    set_a = {1, 2, 3}
    # Two methods of shallow copy
    # set_b = set(set_a)
    set_b = copy(set_a)
    print('----- set')
    print("id of a:{} , id of b: {}".format(id(set_a), id(set_b)))
    # id of a:2099885540520 , id of b: 2099888490984
    print(set_a == set_b)  # True
    print(set_a is set_b)  # False
    # list
    list_a = [1, 2, 3]
    # Three methods of shallow copy
    # list_b = list_a[:]
    # list_b = list(list_b)
    list_b = copy(list_a)
    print('----- list')
    print("id of a:{} , id of b: {}".format(id(list_a), id(list_b)))
    # id of a:2099888478280 , id of b: 2099888478472
    print(list_a == list_b)  # True
    print(list_a is list_b)  # False
    # Python小白學(xué)習(xí)交流群:711312441
    # dict
    dict_a = {"name": "xxx", "age": "123"}
    # Two methods of shallow copy
    # dict_b = dict(dict_a)
    dict_b = copy(dict_a)
    print('----- dict')
    print("id of a:{} , id of b: {}".format(id(dict_a), id(dict_b)))
    # id of a:2099855880480 , id of b: 2099886881024
    print(dict_a == dict_b)  # True
    print(dict_a is dict_b)  # False

這里有一點(diǎn)需要注意,對(duì)于 string 和 number 來說,正如上面提到的 Cpython 做了相應(yīng)的優(yōu)化,讓不同的變量指向了相同的內(nèi)存地址,進(jìn)而 id 的值是相等的。

但對(duì)于元組這個(gè) immutable 元素來說,執(zhí)行 淺拷貝時(shí),也不會(huì)創(chuàng)建一個(gè)內(nèi)存區(qū)域,只是返回一個(gè)老元組的引用。

對(duì)于其他的 mutable 對(duì)象,在淺拷貝后都會(huì)創(chuàng)建一個(gè)新的內(nèi)存區(qū)域,包含了被拷貝元素的引用。

淺拷貝正如它的名字那樣,當(dāng)拷貝嵌套的 mutable 元素時(shí),就會(huì)出現(xiàn)問題:

def shallow_copy_change_value():
    # list
    list_a = [1, 2, 3, [4, 5, 6]]
    list_b = copy(list_a)
    list_a[0] = 10
    list_a[3].append(7)
    print('----- list')
    print("ia:{} ,b: {}".format(list_a, list_b))
    print("id of a:{} , id of b: {}".format(id(list_a), id(list_b)))
    # a:[10, 2, 3, [4, 5, 6, 7]] ,b: [1, 2, 3, [4, 5, 6, 7]]
    # id of a:1698595158472 , id of b: 1698595159752
    print(list_a == list_b)  # False
    print(list_a is list_b)  # False

下面是對(duì)上面 list 淺拷貝的圖解:

執(zhí)行淺拷貝操作:

72a62de32295d24a41f15aecb2f65eac

在 list_b 執(zhí)行淺拷貝后,創(chuàng)建一個(gè)新的對(duì)象,新對(duì)象中的 list_a[0] 指向 1.

修改 list_a 操作:

f82e60e92a6c04ab94146965ee4e985e

當(dāng)執(zhí)行 list_a[0] = 10 操作時(shí),由于 list_a[0] 本身是 number 類型,會(huì)重新創(chuàng)建一塊區(qū)域,用于保存新的值 10. 而新創(chuàng)建的 list_b[0] 并不會(huì)受到影響,還會(huì)指向之前的內(nèi)存區(qū)域。

當(dāng)修改list_a[3] 操作時(shí),由于list_a[3] 在淺拷貝后,新創(chuàng)建的對(duì)象中不會(huì) 嵌套創(chuàng)建 一個(gè)新的 list_a[3] 對(duì)象,僅僅是指向了之前的 list_a[3] 對(duì)象。所以當(dāng)修改 list_a[3] 時(shí), list_b[3] 也會(huì)收到影響。

9.深拷貝

對(duì)于深拷貝操作來說,除了會(huì)創(chuàng)建一個(gè)新的對(duì)象外,會(huì)還遞歸的遍歷老對(duì)象的中的嵌套元素,并形成新的副本。

def shallow_deepcopy_change_value():
    # list
    list_a = [1, 2, 3, [4, 5, 6]]
    list_b = deepcopy(list_a)
    list_a[0] = 10
    list_a[3].append(7)
    print('----- list')
    print("a:{} ,b: {}".format(list_a, list_b))
    print("id of a:{} , id of b: {}".format(id(list_a), id(list_b)))
    # id of a:2099888478280 , id of b: 2099888478472
    print(list_a == list_b)  # False
    print(list_a is list_b)  # False

下面是對(duì)應(yīng)圖解過程:

執(zhí)行深拷貝操作:

4a050f660c31f509ecde59eb8b589c38

修改 list_a 操作:

cd944895e4e8cb41278113baa1906af9

這里 list_a 和 list_b 已經(jīng)是完全的不同的兩個(gè)對(duì)象。

總結(jié)

在這篇文章中,主要介紹了 Python 中對(duì)象,以及對(duì)象的拷貝過程,主要有下面幾個(gè)重要的內(nèi)容:

  • Python 中變量沒有類型,僅僅可看做一個(gè)指針,通過引用指向?qū)ο?。變量可以刪除,但對(duì)象不行。
  • Python 對(duì)象被創(chuàng)建后,會(huì)擁有 identity,type 和 value 三個(gè)屬性。
  • immutable 和 mutable,主要在于 value 在其生命周期內(nèi)是否能發(fā)生變化。
  • 修改 mutable 對(duì)象時(shí),所有指向它的變量都會(huì)受到影響。修改 immutable 對(duì)象時(shí),指向它的其他變量沒有影響。
  • immutable 的大多數(shù)對(duì)象都是 hashable,但要考慮 immutable containers 的特殊情況。
  • 淺拷貝會(huì)創(chuàng)建一個(gè)新的內(nèi)存區(qū)域(對(duì)象),但其內(nèi)部是對(duì)原對(duì)象內(nèi)部引用的拷貝,在使用 mutable 對(duì)象時(shí),存在一定的風(fēng)險(xiǎn)。
  • 深拷貝不但會(huì)創(chuàng)建一個(gè)新的內(nèi)存區(qū)域(對(duì)象),還會(huì)遞歸的創(chuàng)建原對(duì)象的所有嵌套對(duì)象,但也帶來了一些效率的問題。

以上就是Python 對(duì)象拷貝及深淺拷貝區(qū)別的詳細(xì)教程示例的詳細(xì)內(nèi)容,更多關(guān)于Python 對(duì)象拷貝的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論