淺談Python3.6版本的幾種拷貝方式

博文章節


本機環境介紹

編碼語言(Python)

(py3env) C:\Users\XXX\iCloudDrive\PycharmProjects\SAT>python -V
Python 3.6.2

什麼是“不可變/可變數據類型”

Python:一切皆爲對象,一切皆爲對象的引用. 通俗的講有以下區別:

  • 不可變數據類型(int string float tuple)
    不允許變量的值發生改變,如果改變了變量的值,相當於是新建了一個對象。而對於相同的值的對象,在內存中則只有一個對象,內部會有一個引用計數來記錄有多少個變量引用這個對象。
  • 可變數據模型(list dict)
    允許變量的值發生變化,即如果對變量進行append、+=等這種操作後,只是改變了變量的值,而不會新建一個對象,變量引用的對象的地址也不會變化,不過對於相同的值的不同對象,在內存中則會存在不同的對象,即每個對象都有自己的地址,相當於內存中對於同值的對象保存了多份,這裏不存在引用計數,是實實在在的對象。

通過等號“=”拷貝

  • 不可變數據類型
# -*- coding: utf-8 -*-

if __name__ == '__main__':
    originalStr = 'nanjing'
    anotherStr = originalStr
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalStr), id(anotherStr)))
    originalStr = 'beijing'
    print('修改後的內存地址ID爲: {}'.format(id(originalStr)))
    print('原字符串: nanjing, 現字符串: {}'.format(originalStr))
---
複製前後的內存地址ID爲: 36106056, 36106056
修改後的內存地址ID爲: 36106112
原字符串: nanjing, 現字符串: beijing
  • 可變數據類型
# -*- coding: utf-8 -*-

if __name__ == '__main__':
    originalList = ['nanjing', [1, 2]]
    anotherList = originalList
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalList), id(anotherList)))
    originalList[0] = 'beijing'
    originalList[1].append(3)
    print('修改後的內存地址ID爲: {}'.format(id(originalList)))
    print("原列表: ['nanjing', [1, 2]], 現列表: {}".format(originalList))
---
複製前後的內存地址ID爲: 44647176, 44647176
修改後的內存地址ID爲: 44647176
原列表: ['nanjing', [1, 2]], 現列表: ['beijing', [1, 2, 3]]

通過工廠方法拷貝

  • 不可變數據類型
# -*- coding: utf-8 -*-

if __name__ == '__main__':
    originalStr = 'nanjing'
    anotherStr = str(originalStr)
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalStr), id(anotherStr)))
    originalStr = 'beijing'
    print('修改後的內存地址ID爲: {}'.format(id(originalStr)))
    print("原字符串: nanjing, 現字符串: {}".format(originalStr))
---
複製前後的內存地址ID爲: 35909448, 35909448
修改後的內存地址ID爲: 35909504
原字符串: nanjing, 現字符串: beijing
  • 可變數據類型
# -*- coding: utf-8 -*-

if __name__ == '__main__':
    originalList = ['nanjing', [1, 2]]
    anotherList = list(originalList)
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalList), id(anotherList)))
    originalList[0] = 'beijing'
    originalList[1].append(3)
    print('修改後的內存地址ID爲: {}'.format(id(originalList)))
    print("原列表: ['nanjing', [1, 2]], 現列表: {}".format(originalList))
---
複製前後的內存地址ID爲: 38290184, 44809224
修改後的內存地址ID爲: 38290184
原列表: ['nanjing', [1, 2]], 現列表: ['beijing', [1, 2, 3]]

通過”:”(值傳遞)拷貝

  • 不可變數據類型(不涉及)

  • 可變數據類型

# -*- coding: utf-8 -*-

if __name__ == '__main__':
    originalList = ['nanjing', [1, 2]]
    anotherList = originalList[:]
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalList), id(anotherList)))
    originalList[0] = 'beijing'
    originalList[1].append(3)
    print('修改後的內存地址ID爲: {}'.format(id(originalList)))
    print("原列表: ['nanjing', [1, 2]], 現列表: {}".format(originalList))
---
複製前後的內存地址ID爲: 44843784, 44973640
修改後的內存地址ID爲: 44843784
原列表: ['nanjing', [1, 2]], 現列表: ['beijing', [1, 2, 3]]

通過淺拷貝(copy)方法拷貝

  • 不可變數據類型
# -*- coding: utf-8 -*-
import copy

if __name__ == '__main__':
    originalStr = 'nanjing'
    anotherStr = copy.copy(originalStr)
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalStr), id(anotherStr)))
    originalStr = 'beijing'
    print('修改後的內存地址ID爲: {}'.format(id(originalStr)))
    print("原字符串: nanjing, 現字符串: {}".format(originalStr))
---
複製前後的內存地址ID爲: 32042824, 32042824
修改後的內存地址ID爲: 32042880
原字符串: nanjing, 現字符串: beijing
  • 可變數據類型
# -*- coding: utf-8 -*-
import copy

if __name__ == '__main__':
    originalList = ['nanjing', [1, 2]]
    anotherList = copy.copy(originalList)
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalList), id(anotherList)))
    originalList[0] = 'beijing'
    originalList[1].append(3)
    print('修改後的內存地址ID爲: {}'.format(id(originalList)))
    print("原列表: ['nanjing', [1, 2]], 現列表: {}".format(originalList))
---
複製前後的內存地址ID爲: 45103688, 45102984
修改後的內存地址ID爲: 45103688
原列表: ['nanjing', [1, 2]], 現列表: ['beijing', [1, 2, 3]]

通過深拷貝(deepcopy)方法拷貝

  • 不可變數據類型
# -*- coding: utf-8 -*-
import copy

if __name__ == '__main__':
    originalStr = 'nanjing'
    anotherStr = copy.deepcopy(originalStr)
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalStr), id(anotherStr)))
    originalStr = 'beijing'
    print('修改後的內存地址ID爲: {}'.format(id(originalStr)))
    print("原字符串: nanjing, 現字符串: {}".format(originalStr))
----
複製前後的內存地址ID爲: 32370504, 32370504
修改後的內存地址ID爲: 32370560
原字符串: nanjing, 現字符串: beijing
  • 可變數據類型
# -*- coding: utf-8 -*-
import copy

if __name__ == '__main__':
    originalList = ['nanjing', [1, 2]]
    anotherList = copy.deepcopy(originalList)
    print('複製前後的內存地址ID爲: {}, {}'.format(id(originalList), id(anotherList)))
    originalList[0] = 'beijing'
    originalList[1].append(3)
    print('修改後的內存地址ID爲: {}'.format(id(originalList)))
    print("原列表: ['nanjing', [1, 2]], 現列表: {}".format(originalList))
---
複製前後的內存地址ID爲: 37632584, 37631880
修改後的內存地址ID爲: 37632584
原列表: ['nanjing', [1, 2]], 現列表: ['beijing', [1, 2, 3]]

最後總結

  • 通過等號拷貝:可變/不可變數據類型內存地址相同
  • 通過工廠方法拷貝:不可變數據類型內存地址相同,可變數據類型內存地址不同
  • 通過值傳遞拷貝:不可變數據類型不涉及,可變數據類型內存地址不同
  • 通過淺拷貝方法拷貝:不可變數據類型內存地址相同,可變數據類型內存地址不同
  • 通過深拷貝方法拷貝:不可變數據類型內存地址相同,可變數據類型內存地址不同


不可變數據類型,不論什麼方式拷貝,內存地址都相同;
可變數據類型,除等號拷貝,內存地址都不同;
不可變數據類型,修改值後,內存地址改變;
可變數據類型,修改值後,內存地址不改變;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章