Skip to content

[Python] 使用 copy 模組複製物件

在 Python 中最容易導致程式錯誤的原因之一,就是 List 物件內值的意外改變。很多人剛遇到這個問題,臉上的表情就好像見鬼了一般,可能還會嚷嚷 Python 壞掉了也不一定 XD

一個最最簡單的例子,就是 List 物件跟著賦予的來源一起進行了更動。以下看個簡單的範例:

# coding: utf-8


def main():
    # Init
    a = [[1, 1], [2, 2]]
    b = a

    # Append
    a.append([3, 3])

    # Print
    print('a:', a)
    print('b:', b)


if __name__ == '__main__':
    main()



Output:

a: [[1, 1], [2, 2], [3, 3]]
b: [[1, 1], [2, 2], [3, 3]]

很奇怪,對吧?明明增加了新值的只有 a 變數啊!

這個原因其實很單純,因為 b 只是一個共享 a 值的新變數名稱罷了。我們可以使用 id() 函式瞧瞧:

# coding: utf-8


def main():
    # Init
    a = [[1, 1], [2, 2]]
    b = a

    # Append
    a.append([3, 3])

    # Print
    print('a id:', id(a))
    print('b id:', id(b))


if __name__ == '__main__':
    main()



Output:

a id: 140219101052168
b id: 140219101052168

會發現,a 和 b 真的是同樣的值,只是變數名稱不一樣罷了。

但這很麻煩啊,通常我們撰寫程式並不希望使用兩個同樣值卻不一樣名稱的變數,而是希望 b 能『複製』a 的值,但以後不論增減值,a 和 b 都是毫不相關的。

這時候,就輪到 Python 中的 copy 模組登場了。


淺複製(Shallow)與深複製(Deep)

Python 的 copy 模組中,有分成淺複製shallow copy)以及深複製deep copy)兩類。


淺複製(shallow copy)

『淺複製』使用 copy() 函式,能夠讓複製的物件獨立,也就是 a 和 b 已經無關了,新增新的值也不會影響到 b。

# coding: utf-8
import copy


def main():
    # Init
    a = [[1, 1], [2, 2]]
    b = copy.copy(a)

    # Change
    a.append([3, 3])

    # Print
    print('a:', a)
    print('a id:', id(a))
    print('='*50)
    print('b:', b)
    print('b id:', id(b))


if __name__ == '__main__':
    main()



Output:

a: [[1, 1], [2, 2], [3, 3]]
a id: 140378660764744
==================================================
b: [[1, 1], [2, 2]]
b id: 140378660765000

但是淺複製跟深複製還是不一樣的。也就是雖然 a 變數新增、刪減元素不會影響到 b,但若是直接更動 a 變數內 b 已經參考的元素,則還是會對 b 產生影響。

# coding: utf-8
import copy


def main():
    # Init
    a = [[1, 1], [2, 2]]
    b = copy.copy(a)

    # Change
    a[0][0] = 0

    # Print
    print('a:', a)
    print('a id:', id(a))
    print('='*50)
    print('b:', b)
    print('b id:', id(b))


if __name__ == '__main__':
    main()



Output:

a: [[0, 1], [2, 2]]
a id: 140223083543624
==================================================
b: [[0, 1], [2, 2]]
b id: 140223083543880

可以看到, [1, 1] 的第一個元素都被改變了。要防止這種事情發生,只能使用 deepcopy()


深複製(deep copy)

『深複製』使用 deepcopy() 函式,能讓 a 和 b 兩個變數完全分離,是徹徹底底地複製。

# coding: utf-8
import copy


def main():
    # Init
    a = [[1, 1], [2, 2]]
    b = copy.deepcopy(a)

    # Change
    a.append([3, 3])
    a[0][0] = 0

    # Print
    print('a:', a)
    print('a id:', id(a))
    print('='*50)
    print('b:', b)
    print('b id:', id(b))


if __name__ == '__main__':
    main()



Output:

a: [[0, 1], [2, 2], [3, 3]]
a id: 140719924018248
==================================================
b: [[1, 1], [2, 2]]
b id: 140719924018504

可以看到,這次就算修改 a 中的元素也不會影響到 b 了。


References


Read More

Tags:

Leave a Reply