วิธีคัดลอกพจนานุกรมและแก้ไขเฉพาะสำเนา


855

ใครช่วยอธิบายสิ่งนี้ให้ฉันได้ไหม นี่ไม่สมเหตุสมผลเลยสำหรับฉัน

ฉันคัดลอกพจนานุกรมไปที่อื่นและแก้ไขครั้งที่สองและทั้งคู่เปลี่ยนไป ทำไมสิ่งนี้จึงเกิดขึ้น

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict1
>>> dict2
{'key2': 'value2', 'key1': 'value1'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key2': 'WHY?!', 'key1': 'value1'}

4
PythonTutorเหมาะอย่างยิ่งสำหรับการแสดงภาพอ้างอิง Python นี่คือรหัสนี้ในขั้นตอนสุดท้าย คุณสามารถเห็นdict1และdict2ชี้ไปที่ dict เดียวกัน
wjandrea

คำตอบ:


883

Python จะไม่คัดลอกวัตถุโดยปริยาย เมื่อคุณตั้งค่าdict2 = dict1คุณกำลังทำให้พวกเขาอ้างถึงวัตถุ dict ที่แน่นอนเหมือนกันดังนั้นเมื่อคุณกลายพันธุ์การอ้างอิงทั้งหมดที่อ้างอิงไปยังวัตถุนั้นจะอยู่ในสถานะปัจจุบัน

หากคุณต้องการคัดลอก dict (ซึ่งหายาก) คุณต้องทำอย่างชัดเจนด้วย

dict2 = dict(dict1)

หรือ

dict2 = dict1.copy()

26
อาจเป็นการดีกว่าถ้าจะพูดว่า "dict2 และ dict1 ชี้ไปที่พจนานุกรมเดียวกัน " คุณไม่ได้เปลี่ยน dict1 หรือ dict2 แต่เป็นสิ่งที่ชี้ไป
GrayWizardx

275
นอกจากนี้โปรดทราบว่า dict.copy () นั้นตื้นถ้ามีรายการ / etc ซ้อนอยู่ในนั้นการเปลี่ยนแปลงจะถูกนำไปใช้กับทั้งสอง IIRC Deepcopy จะหลีกเลี่ยงสิ่งนั้น
Will

16
มันค่อนข้างไม่ถูกต้องที่ไพ ธ อนไม่เคยคัดลอกวัตถุโดยปริยาย ชนิดข้อมูลดั้งเดิมเช่น int, float และ bool ก็จะถือว่าเป็นออบเจ็กต์ (เพียงแค่ทำdir(1)เพื่อดูว่า) แต่จะถูกคัดลอกโดยปริยาย
daniel kullmann

17
@danielkullmann ฉันคิดว่าคุณอาจเข้าใจผิดเกี่ยวกับ Python โดยพิจารณาจากภาษาอื่น ๆ ที่คุณจัดการกับงาน ใน Python ก) ไม่มีแนวคิดของ "ชนิดข้อมูลดั้งเดิม" int, floatและboolอินสแตนซ์เป็นวัตถุ Python จริงและ b) วัตถุประเภทเหล่านี้จะไม่ถูกคัดลอกโดยปริยายเมื่อคุณผ่านวัตถุเหล่านั้นไม่ใช่ในระดับความหมายของ Python อย่างแน่นอนและไม่เป็นรายละเอียดการนำไปปฏิบัติใน CPython
Mike Graham

39
สำนวนโวหารที่ไม่พร้อมเพรียงเช่น "สำเนาลึกถือว่าเป็นอันตราย" ไม่ช่วยเหลือ สิ่งอื่นที่เท่าเทียมกันการคัดลอกโครงสร้างข้อมูลที่ซับซ้อนนั้นมีแนวโน้มที่จะทำให้เกิดปัญหากรณีขอบที่ไม่คาดคิดมากกว่าการคัดลอกโครงสร้างเดียวกันในระดับลึก สำเนาที่การดัดแปลงแก้ไขวัตถุต้นฉบับไม่ใช่สำเนา มันเป็นข้อผิดพลาด Ergo ส่วนใหญ่กรณีการใช้งานอย่างควรโทรcopy.deepcopy()มากกว่าหรือdict() คำตอบสั้น ๆของImranอยู่ทางด้านขวาของสติแตกต่างจากคำตอบนี้ dict.copy()
เซซิลแกง

647

เมื่อคุณกำหนดdict2 = dict1คุณจะไม่ทำสำเนาของdict1มันจะส่งผลในการเป็นเพียงแค่ชื่ออีกdict2dict1

หากต้องการคัดลอกประเภทที่ไม่แน่นอนเช่นพจนานุกรมให้ใช้copy/ deepcopyของcopyโมดูล

import copy

dict2 = copy.deepcopy(dict1)

80
สำหรับพจนานุกรมใด ๆ ที่ฉันทำงานด้วย Deepcopy คือสิ่งที่ฉันต้องการ ... ฉันเพิ่งเสียเวลาไปหลายชั่วโมงเนื่องจากข้อผิดพลาดที่เป็นเพราะฉันไม่ได้รับสำเนาที่สมบูรณ์ของพจนานุกรมที่ซ้อนกันและการเปลี่ยนแปลงรายการที่ซ้อนกันมีผลกับต้นฉบับ .
flutefreak7

7
กันที่นี่ Deepcopy () ทำเคล็ดลับ ทำให้ข้อความที่ซ้อนกันของฉันซ้อนอยู่ในแคชที่หมุนได้โดยเพิ่มการประทับเวลาใน 'การคัดลอก' ของเหตุการณ์ดั้งเดิม ขอบคุณ!
fxstein

8
จริง ๆ แล้วควรถูกทำเครื่องหมายเป็นคำตอบที่ถูกต้อง คำตอบนี้เป็นคำทั่วไปและใช้ได้สำหรับพจนานุกรมของพจนานุกรมเช่นกัน
orezvani

30
นี่ควรเป็นคำตอบที่ยอมรับได้ "สำเนาลึกถือเป็นอันตราย" สำนวนที่ไม่พร้อมเพรียงฝังอยู่ในส่วนความคิดเห็นของคำตอบที่ได้รับการยอมรับในปัจจุบันเชิญโจ่งแจ้งประสาน woes เมื่อคัดลอกพจนานุกรมซ้อน (เช่นเอกสารที่นี่) และควรจะท้าทายเช่นนี้
เซซิลแกง

Deepcopy เป็นวิธีที่จะไปในกรณีของโครงสร้างพจนานุกรมที่ซับซ้อน dict1.copy () เพียงแค่คัดลอกค่าของคีย์เป็นการอ้างอิงและไม่เป็นวัตถุ
Rohith N

182

ในขณะที่dict.copy()และdict(dict1)สร้างสำเนาพวกเขาเป็นเพียงสำเนาตื้น ถ้าคุณต้องการลึกสำเนาcopy.deepcopy(dict1)ถูกต้อง ตัวอย่าง:

>>> source = {'a': 1, 'b': {'m': 4, 'n': 5, 'o': 6}, 'c': 3}
>>> copy1 = x.copy()
>>> copy2 = dict(x)
>>> import copy
>>> copy3 = copy.deepcopy(x)
>>> source['a'] = 10  # a change to first-level properties won't affect copies
>>> source
{'a': 10, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> copy3
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}
>>> source['b']['m'] = 40  # a change to deep properties WILL affect shallow copies 'b.m' property
>>> source
{'a': 10, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy1
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy2
{'a': 1, 'c': 3, 'b': {'m': 40, 'o': 6, 'n': 5}}
>>> copy3  # Deep copy's 'b.m' property is unaffected
{'a': 1, 'c': 3, 'b': {'m': 4, 'o': 6, 'n': 5}}

เกี่ยวกับการคัดลอกแบบตื้นและลึกจากPython copymodule docs :

ความแตกต่างระหว่างการทำสำเนาแบบตื้นและลึกนั้นเกี่ยวข้องเฉพาะกับวัตถุแบบผสม (วัตถุที่มีวัตถุอื่น ๆ เช่นรายการหรืออินสแตนซ์ของคลาส):

  • สำเนาตื้นสร้างวัตถุประกอบใหม่แล้ว (เท่าที่เป็นไปได้) แทรกการอ้างอิงลงในวัตถุที่พบในต้นฉบับ
  • การทำสำเนาแบบลึกจะสร้างวัตถุผสมใหม่จากนั้นแทรกการทำสำเนาซ้ำลงในวัตถุที่พบในต้นฉบับ

2
นี่ควรเป็นคำตอบที่ถูกต้องเนื่องจากจะไม่วนลูปอย่างชัดเจนและสามารถใช้สำหรับโครงสร้างหลักอื่น ๆ ได้
Nikkolasg

27
เพียงชี้แจง: w=copy.deepcopy(x)เป็นสายสำคัญ
alcoholiday

ความแตกต่างระหว่างdict2 = dict1และdict2 = copy.deepcopy(dict1)คืออะไร?
TheTank

1
@TheTank, y = x ทำให้ทั้งสองชื่อ (การอ้างอิง) อ้างถึงวัตถุเดียวกันคือ "y is x" คือ True การเปลี่ยนแปลงใด ๆ ที่ทำกับวัตถุผ่าน x จะเท่ากับการเปลี่ยนแปลงเดียวกันผ่าน y อย่างไรก็ตาม u, v, w เป็นการอ้างอิงไปยังวัตถุต่าง ๆ ใหม่ซึ่งมีค่าคัดลอกมาจาก x ในระหว่างการสร้างอินสแตนซ์ สำหรับความแตกต่างระหว่าง u, v (copy ตื้น) และ w (deepcopy) โปรดตรวจสอบdocs.python.org/2/library/copy.html
gpanda

63

บน python 3.5+ มีวิธีที่ง่ายกว่าในการทำสำเนาตื้นโดยใช้โอเปอเรเตอร์ unpackaging ** กำหนดโดยห้าวหาญ 448

>>>dict1 = {"key1": "value1", "key2": "value2"}
>>>dict2 = {**dict1}
>>>print(dict2)
{'key1': 'value1', 'key2': 'value2'}
>>>dict2["key2"] = "WHY?!"
>>>print(dict1)
{'key1': 'value1', 'key2': 'value2'}
>>>print(dict2)
{'key1': 'value1', 'key2': 'WHY?!'}

** แยกพจนานุกรมออกเป็นพจนานุกรมใหม่ที่กำหนดให้เป็น dict2

นอกจากนี้เรายังสามารถยืนยันได้ว่าพจนานุกรมแต่ละรายการมีรหัสที่แตกต่างกัน

>>>id(dict1)
 178192816

>>>id(dict2)
 178192600

หากต้องการสำเนาที่ลึกแล้วcopy.deepcopy ()ยังคงเป็นวิธีที่จะไป


3
สิ่งนี้ดูคล้ายกับพอยน์เตอร์ใน C ++ ดีสำหรับการทำงานให้สำเร็จ แต่การอ่านอย่างชาญฉลาดฉันมักจะไม่ชอบตัวดำเนินการประเภทนี้
Ernesto

1
มันมีรูปลักษณ์ c'ish ชนิดหนึ่ง ... แต่เมื่อรวมพจนานุกรมหลายเล่มเข้าด้วยกัน
PabTorre

2
ระวังด้วยว่ามันทำสำเนาได้ตื้นเท่านั้น
เซบาสเตียนเดรสเลอร์

คุณพูดถูก @SebastianDressler ฉันจะทำการปรับเปลี่ยน ขอบคุณ
PabTorre

2
มีประโยชน์หากคุณต้องการสร้างสำเนาด้วย spicies:dict2 = {**dict1, 'key3':'value3'}
evg656e

47

วิธีที่ดีที่สุดและง่ายที่สุดในการสร้างสำเนาของdictในPython 2.7 และ 3คือ ...

วิธีสร้างสำเนาของพจนานุกรมอย่างง่าย (ระดับเดียว):

1.ใช้เมธอดdict ()แทนที่จะสร้างการอ้างอิงที่ชี้ไปยัง dict ที่มีอยู่

my_dict1 = dict()
my_dict1["message"] = "Hello Python"
print(my_dict1)  # {'message':'Hello Python'}

my_dict2 = dict(my_dict1)
print(my_dict2)  # {'message':'Hello Python'}

# Made changes in my_dict1 
my_dict1["name"] = "Emrit"
print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2)  # {'message':'Hello Python'}

2.การใช้วิธีการอัพเดตในตัว()ของพจนานุกรมไพ ธ อน

my_dict2 = dict()
my_dict2.update(my_dict1)
print(my_dict2)  # {'message':'Hello Python'}

# Made changes in my_dict1 
my_dict1["name"] = "Emrit"
print(my_dict1)  # {'message':'Hello Python', 'name' : 'Emrit'}
print(my_dict2)  # {'message':'Hello Python'}

วิธีสร้างสำเนาของพจนานุกรมที่ซ้อนกันหรือซับซ้อน:

ใช้โมดูลการคัดลอกในตัวซึ่งมีการดำเนินการคัดลอกตื้นและลึกทั่วไป โมดูลนี้มีอยู่ใน Python 2.7 และ 3 *

import copy

my_dict2 = copy.deepcopy(my_dict1)

6
ฉันเชื่อว่าdict()สร้างสำเนาตื้น ๆ ไม่ใช่สำเนาลึก หมายความว่าหากคุณมีซ้อนกันอยู่dictด้านนอกdictจะเป็นสำเนา แต่ dict ภายในจะอ้างอิงถึง dict ภายในเดิม
shmuels

@shmuels ใช่ทั้งสองวิธีนี้จะสร้างสำเนาตื้นไม่ใช่ลึก ดูคำตอบที่อัพเดต
AKay Nirala

37

คุณสามารถสร้างพจนานุกรมใหม่ด้วยความเข้าใจในพจนานุกรม วิธีนี้จะหลีกเลี่ยงการนำเข้าสำเนา

dout = dict((k,v) for k,v in mydict.items())

แน่นอนใน python> = 2.7 คุณสามารถทำได้:

dout = {k:v for k,v in mydict.items()}

แต่สำหรับการย้อนกลับเข้ากันได้วิธีการที่ดีกว่า


4
สิ่งนี้มีประโยชน์อย่างยิ่งหากคุณต้องการควบคุมวิธีคัดลอกและคัดลอกอย่างแม่นยำมากขึ้น +1
ApproachingDarknessFish

14
โปรดทราบว่าวิธีนี้ไม่ได้ทำการคัดลอกแบบลึกและหากคุณต้องการคัดลอกตื้นโดยไม่จำเป็นต้องควบคุมปุ่มที่จะคัดลอกd2 = dict.copy(d1)ก็ไม่จำเป็นต้องนำเข้าใด ๆ
Jarek Piórkowski

1
@ JarekPiórkowski: หรือคุณสามารถเรียกวิธีการเช่นเดียวกับวิธี:d2 = d1.copy()
Azat Ibrakov

โปรดทราบว่าคุณไม่จำเป็นต้องมีความเข้าใจในตัวอย่างแรก dict.itemsส่งคืนคู่คีย์ / ค่า iterable แล้ว ดังนั้นคุณสามารถใช้dict(mydict.items())(คุณยังสามารถใช้dict(mydict)) มันอาจมีประโยชน์ที่จะเข้าใจถ้าคุณต้องการกรองรายการ
Paul Rooney

22

นอกเหนือจากโซลูชันอื่น ๆ ที่มีให้คุณสามารถใช้**เพื่อรวมพจนานุกรมเข้ากับพจนานุกรมที่ว่างเปล่าเช่น

shallow_copy_of_other_dict = {**other_dict}.

ตอนนี้คุณจะมี "ตื้น" other_dictสำเนา

นำไปใช้กับตัวอย่างของคุณ:

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = {**dict1}
>>> dict2
{'key1': 'value1', 'key2': 'value2'}
>>> dict2["key2"] = "WHY?!"
>>> dict1
{'key1': 'value1', 'key2': 'value2'}
>>>

ตัวชี้: ความแตกต่างระหว่าง copys ตื้นและลึก


1
ผลลัพธ์นี้ในสำเนาตื้นไม่ใช่สำเนาลึก
sytech

1
ฉันพยายามทำสิ่งนี้ แต่มีปัญหา ใช้งานได้กับ python 3.5 ขึ้นไปเท่านั้น python.org/dev/peps/pep-0448
ThatGuyRob

19

คำสั่งการมอบหมายใน Python ไม่ได้คัดลอกวัตถุพวกมันสร้างการเชื่อมโยงระหว่างเป้าหมายและวัตถุ

ดังนั้นdict2 = dict1จะมีผลผูกพันระหว่างdict2และวัตถุที่dict1อ้างถึงอีก

ถ้าคุณต้องการคัดลอก Dict copy moduleคุณสามารถใช้ โมดูลคัดลอกมีสองอินเตอร์เฟส:

copy.copy(x)
Return a shallow copy of x.

copy.deepcopy(x)
Return a deep copy of x.

ความแตกต่างระหว่างการทำสำเนาแบบตื้นและลึกนั้นเกี่ยวข้องเฉพาะกับวัตถุแบบผสม (วัตถุที่มีวัตถุอื่น ๆ เช่นรายการหรืออินสแตนซ์ของคลาส):

สำเนาตื้นสร้างวัตถุสารประกอบใหม่แล้ว (เท่าที่เป็นไปได้) แทรกอ้างอิงเป็นมันไปยังวัตถุที่พบในที่เดิม

การทำสำเนาแบบลึกจะสร้างวัตถุผสมใหม่จากนั้นแทรกการทำสำเนาซ้ำลงในวัตถุที่พบในต้นฉบับ

ตัวอย่างเช่นใน python 2.7.9:

>>> import copy
>>> a = [1,2,3,4,['a', 'b']]
>>> b = a
>>> c = copy.copy(a)
>>> d = copy.deepcopy(a)
>>> a.append(5)
>>> a[4].append('c')

และผลลัพธ์คือ:

>>> a
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> b
[1, 2, 3, 4, ['a', 'b', 'c'], 5]
>>> c
[1, 2, 3, 4, ['a', 'b', 'c']]
>>> d
[1, 2, 3, 4, ['a', 'b']]

10

คุณสามารถคัดลอกและแก้ไขสำเนาที่สร้างขึ้นใหม่ในครั้งเดียวโดยเรียกตัวdictสร้างด้วยอาร์กิวเมนต์คำหลักเพิ่มเติม:

>>> dict1 = {"key1": "value1", "key2": "value2"}
>>> dict2 = dict(dict1, key2="WHY?!")
>>> dict1
{'key2': 'value2', 'key1': 'value1'}
>>> dict2
{'key2': 'WHY?!', 'key1': 'value1'}

9

สิ่งนี้ทำให้ฉันสับสนในตอนแรกเพราะฉันมาจากภูมิหลัง C

ใน C ตัวแปรคือตำแหน่งในหน่วยความจำที่มีชนิดที่กำหนดไว้ การกำหนดให้ตัวแปรทำสำเนาข้อมูลลงในตำแหน่งหน่วยความจำของตัวแปร

แต่ใน Python ตัวแปรทำหน้าที่เหมือนกับตัวชี้ไปยังวัตถุ ดังนั้นการกำหนดตัวแปรหนึ่งให้กับอีกตัวแปรหนึ่งไม่ได้ทำสำเนามันแค่ทำให้ชื่อตัวแปรนั้นชี้ไปที่วัตถุเดียวกัน


5
ตัวแปร python ทำหน้าที่เหมือนกับการอ้างอิง c ++ เพิ่มเติม
Ruggero Turra

7
เพราะทุกอย่างใน Python นั้นเป็นวัตถุ! diveintopython.net/getting_to_know_python/... (ใช่การตอบสนองนี้เป็นเวลาหลายปีปลาย แต่บางทีมันอาจจะเป็นเรื่องของการใช้งานบางอย่างเพื่อใครบางคน!)
grimman

1
ฉันเชื่อว่าความหมายของภาษา Python บอกว่าไม่มี "ตัวแปร" พวกเขาถูกเรียกว่า "การอ้างอิงที่มีชื่อ"; หมายถึงการอ้างอิงไปยังวัตถุเป็นสตริงประโยคในรหัส วัตถุสามารถมีการอ้างอิงชื่อมาก วัตถุที่ไม่เปลี่ยนรูปเช่น ints และ floats และ str instance มีเพียงหนึ่งอินสแตนซ์ของมันต่อกระบวนการ int ของ 1 ในหน่วยความจำจะไม่เปลี่ยนเป็น 2 หรือค่าอื่น ๆ ณ ที่อยู่หน่วยความจำเดียวกันเมื่อคุณทำ myvalue นี้ = 1 myvalue = 2
DevPlayer

7

ตัวแปรทุกตัวในไพ ธ อน (สิ่งที่ต้องการdict1หรือstrหรือ__builtins__เป็นตัวชี้ไปยัง "วัตถุ" ที่ซ่อนอยู่ภายในเครื่อง

หากคุณตั้งdict1 = dict2คุณเพียงแค่ชี้dict1ไปยังวัตถุเดียวกัน (หรือที่ตั้งของหน่วยความจำหรือสิ่งที่คล้ายคลึงกันที่คุณต้องการ) dict2เป็น ตอนนี้วัตถุอ้างอิงโดยเป็นวัตถุเดียวกันโดยอ้างอิงจากdict1dict2

คุณสามารถตรวจสอบ: ควรจะเป็นdict1 is dict2 Trueนอกจากนี้ควรจะเป็นเช่นเดียวกับid(dict1)id(dict2)

คุณต้องการหรือdict1 = copy(dict2)dict1 = deepcopy(dict2)

ความแตกต่างระหว่างcopyและdeepcopy? deepcopyจะทำให้แน่ใจว่าองค์ประกอบของdict2(คุณชี้ไปที่รายการหรือไม่) เป็นสำเนาด้วย

ฉันไม่ได้ใช้deepcopyอะไรมาก - โดยปกติแล้วการเขียนโค้ดที่ไม่จำเป็นต้องใช้มันเป็นเรื่องปกติ


ฉันเพิ่งรู้ว่าฉันต้องใช้ deepcopy อยู่เสมอเพื่อที่เมื่อฉันคัดลอกพจนานุกรมที่ซ้อนกันและเริ่มแก้ไขรายการที่ซ้อนกันเอฟเฟกต์จะเกิดขึ้นเฉพาะในการคัดลอกไม่ใช่ต้นฉบับ
flutefreak7

6

dict1เป็นสัญลักษณ์ที่อ้างอิงถึงวัตถุพจนานุกรมพื้นฐาน การมอบหมายdict1ให้dict2มอบหมายการอ้างอิงเดียวกันเท่านั้น การเปลี่ยนค่าคีย์ที่ผ่านสัญลักษณ์การเปลี่ยนแปลงวัตถุต้นแบบซึ่งยังมีผลต่อdict2 dict1นี่คือความสับสน

มันง่ายกว่าที่จะให้เหตุผลเกี่ยวกับค่าที่ไม่เปลี่ยนรูปได้มากกว่าการอ้างอิงดังนั้นให้ทำสำเนาเมื่อทำได้:

person = {'name': 'Mary', 'age': 25}
one_year_later = {**person, 'age': 26}  # does not mutate person dict

นี่คือ syntactically เหมือนกับ:

one_year_later = dict(person, age=26)

5

dict2 = dict1ไม่คัดลอกพจนานุกรม มันช่วยให้โปรแกรมเมอร์เป็นวิธีที่สอง ( dict2) เพื่ออ้างถึงพจนานุกรมเดียวกัน


5
>>> dict2 = dict1
# dict2 is bind to the same Dict object which binds to dict1, so if you modify dict2, you will modify the dict1

มีหลายวิธีในการคัดลอกวัตถุ Dict ที่ฉันใช้

dict_1 = {
           'a':1,
           'b':2
         }
dict_2 = {}
dict_2.update(dict_1)

12
dict_2 = dict_1.copy()มีประสิทธิภาพมากขึ้นและมีเหตุผลมากขึ้น
Jean-François Fabre

2
โปรดทราบว่าหากคุณมี dict ภายใน dict1 ด้วย dict_1.copy () การเปลี่ยนแปลงที่คุณทำกับ dict ภายในใน dict_2 จะถูกนำไปใช้กับ dict ภายในใน dict_1 ด้วย ในกรณีนี้คุณควรใช้ copy.deepcopy (dict_1) แทน
เข้าคิว

1

ตามที่คนอื่นได้อธิบายแล้วตัวในเครื่องdictไม่ได้ทำในสิ่งที่คุณต้องการ แต่ใน Python2 (และอาจเป็น 3 ด้วย) คุณสามารถสร้างValueDictคลาสที่คัดลอกได้ง่าย=เพื่อให้คุณมั่นใจได้ว่าต้นฉบับจะไม่เปลี่ยนแปลง

class ValueDict(dict):

    def __ilshift__(self, args):
        result = ValueDict(self)
        if isinstance(args, dict):
            dict.update(result, args)
        else:
            dict.__setitem__(result, *args)
        return result # Pythonic LVALUE modification

    def __irshift__(self, args):
        result = ValueDict(self)
        dict.__delitem__(result, args)
        return result # Pythonic LVALUE modification

    def __setitem__(self, k, v):
        raise AttributeError, \
            "Use \"value_dict<<='%s', ...\" instead of \"d[%s] = ...\"" % (k,k)

    def __delitem__(self, k):
        raise AttributeError, \
            "Use \"value_dict>>='%s'\" instead of \"del d[%s]" % (k,k)

    def update(self, d2):
        raise AttributeError, \
            "Use \"value_dict<<=dict2\" instead of \"value_dict.update(dict2)\""


# test
d = ValueDict()

d <<='apples', 5
d <<='pears', 8
print "d =", d

e = d
e <<='bananas', 1
print "e =", e
print "d =", d

d >>='pears'
print "d =", d
d <<={'blueberries': 2, 'watermelons': 315}
print "d =", d
print "e =", e
print "e['bananas'] =", e['bananas']


# result
d = {'apples': 5, 'pears': 8}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
d = {'apples': 5, 'pears': 8}
d = {'apples': 5}
d = {'watermelons': 315, 'blueberries': 2, 'apples': 5}
e = {'apples': 5, 'pears': 8, 'bananas': 1}
e['bananas'] = 1

# e[0]=3
# would give:
# AttributeError: Use "value_dict<<='0', ..." instead of "d[0] = ..."

โปรดดูที่การปรับเปลี่ยนรูปแบบ lvalue ที่กล่าวถึงที่นี่: งูหลาม 2.7 - ไวยากรณ์สะอาดสำหรับการปรับเปลี่ยน ข้อสังเกตที่สำคัญคือstrและintปฏิบัติตนเป็นค่าใน Python (แม้ว่าพวกเขาจะเป็นวัตถุที่ไม่เปลี่ยนรูปได้จริงภายใต้ประทุน) ขณะที่คุณกำลังสังเกตว่าโปรดสังเกตว่าไม่มีอะไรที่เป็นพิเศษเกี่ยวกับอย่างน่าอัศจรรย์หรือstr สามารถนำไปใช้ในวิธีเดียวกันได้หลายอย่างและฉันสามารถคิดถึงหลาย ๆ กรณีที่เหมาะสมintdictValueDict


0

รหัสต่อไปนี้ซึ่งอยู่ใน dicts ซึ่งเป็นไปตามไวยากรณ์ json มากกว่า 3 ครั้งเร็วกว่า deepcopy

def CopyDict(dSrc):
    try:
        return json.loads(json.dumps(dSrc))
    except Exception as e:
        Logger.warning("Can't copy dict the preferred way:"+str(dSrc))
        return deepcopy(dSrc)

0

ฉันพบพฤติกรรมแปลกประหลาดเมื่อพยายามคัดลอกคุณสมบัติพจนานุกรมของคลาสที่ไม่มีการกำหนดให้กับตัวแปร

new = copy.deepcopy(my_class.a)ไม่ทำงานเช่นการแก้ไขการnewปรับเปลี่ยนmy_class.a

แต่ถ้าคุณทำold = my_class.aแล้วnew = copy.deepcopy(old)มันทำงานได้อย่างสมบูรณ์แบบเช่นการปรับเปลี่ยนnewจะไม่ส่งผลกระทบmy_class.a

ฉันไม่แน่ใจว่าทำไมสิ่งนี้ถึงเกิดขึ้น แต่หวังว่ามันจะช่วยประหยัดเวลาได้หลายชั่วโมง! :)


ดังนั้นคุณจะทำสำเนาลึกของได้my_class.aอย่างไร
Anthony

ไม่ใช่วิธีที่ดีที่สุด การตอบสนองที่ดีคือการร้อง
David Beauchemin

-1

เนื่องจาก dict2 = dict1, dict2 เก็บการอ้างอิงถึง dict1 ทั้ง dict1 และ dict2 ชี้ไปที่ตำแหน่งเดียวกันในหน่วยความจำ นี่เป็นเพียงกรณีปกติในขณะที่ทำงานกับวัตถุที่ไม่แน่นอนในหลาม เมื่อคุณทำงานกับวัตถุที่ไม่แน่นอนในหลามคุณจะต้องระมัดระวังเพราะยากที่จะดีบัก เช่นตัวอย่างดังต่อไปนี้

 my_users = {
        'ids':[1,2],
        'blocked_ids':[5,6,7]
 }
 ids = my_users.get('ids')
 ids.extend(my_users.get('blocked_ids')) #all_ids
 print ids#output:[1, 2, 5, 6, 7]
 print my_users #output:{'blocked_ids': [5, 6, 7], 'ids': [1, 2, 5, 6, 7]}

ตัวอย่างความตั้งใจนี้คือการรับรหัสผู้ใช้ทั้งหมดรวมถึงรหัสที่ถูกบล็อก ที่เราได้รับจากตัวแปร ids แต่เราก็ปรับปรุงค่าของmy_usersโดยไม่ได้ตั้งใจ เมื่อคุณขยายรหัสกับblocked_ids my_users ได้อัปเดตเพราะรหัสหมายถึงmy_users


-1

คัดลอกโดยใช้สำหรับลูป:

orig = {"X2": 674.5, "X3": 245.0}

copy = {}
for key in orig:
    copy[key] = orig[key]

print(orig) # {'X2': 674.5, 'X3': 245.0}
print(copy) # {'X2': 674.5, 'X3': 245.0}
copy["X2"] = 808
print(orig) # {'X2': 674.5, 'X3': 245.0}
print(copy) # {'X2': 808, 'X3': 245.0}

1
ใช้งานได้กับพจนานุกรมง่ายๆเท่านั้น ทำไมไม่ใช้deepcopyซึ่งถูกสร้างขึ้นอย่างชัดเจนเพื่อจุดประสงค์นี้
Anthony

ไม่ใช่วิธีที่ดีที่สุด การตอบสนองที่ดีคือการร้อง
David Beauchemin

-6

คุณสามารถใช้โดยตรง:

dict2 = eval(repr(dict1))

โดยที่ object dict2 เป็นสำเนาอิสระของ dict1 ดังนั้นคุณสามารถแก้ไข dict2 ได้โดยไม่กระทบกับ dict1

สิ่งนี้ใช้ได้กับวัตถุทุกชนิด


4
คำตอบนี้ไม่ถูกต้องและไม่ควรใช้ ตัวอย่างเช่นคลาสที่ผู้ใช้กำหนดอาจไม่มีความเหมาะสมที่__repr__จะถูกสร้างขึ้นใหม่โดย eval และคลาสของวัตถุจะไม่อยู่ในขอบเขตปัจจุบันที่จะถูกเรียก แม้การผสานกับชนิดของบิวด์อินก็จะล้มเหลวถ้าวัตถุเดียวกันนั้นถูกเก็บไว้ภายใต้คีย์หลายตัวเช่นเดียวกับที่dict2มีวัตถุสองชิ้นแยกกัน พจนานุกรมตัวอ้างอิงที่มีตัวเองแทนจะมีdict1 Ellipsisมันจะดีกว่าที่จะใช้dict1.copy()
Eldritch Cheese

วัตถุ (หรือ "ค่า") ไม่ได้รับการคาดหวังว่าจะมีตัวแทนที่ซื่อสัตย์โดยสายอักขระไม่ใช่ในกรณีใด ๆ ที่มนุษย์สามารถอ่านได้
Alexey
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.