ฉันจะคัดลอกสตริง Python ได้อย่างไร


92

ฉันทำนี่:

a = 'hello'

และตอนนี้ฉันต้องการสำเนาอิสระของa:

import copy

b = str(a)
c = a[:]
d = a + ''
e = copy.copy(a)

map( id, [ a,b,c,d,e ] )

ออก [3]:

[4365576160, 4365576160, 4365576160, 4365576160, 4365576160]

ทำไมพวกเขาทั้งหมดมีที่อยู่หน่วยความจำเดียวกันและฉันจะรับสำเนาได้aอย่างไร?


3
เพื่อให้ได้คำตอบที่แตกต่างจาก Martijin (ซึ่งถูกต้องทั้งหมดแม้ว่าจะไม่จำเป็นต้องตอบคำถามตามที่ระบุไว้ก็ตาม) คุณอาจต้องการให้รายละเอียดเพิ่มเติม / กรณีการใช้งานเพื่อแสดงว่าเหตุใดคุณจึงต้องการคัดลอก
elmo

4
ในฐานะที่เป็น @elemo นัยนี้อาจจะมีปัญหา XY
martineau

2
ผมมีความสนใจในการประมาณใช้หน่วยความจำของพจนานุกรมที่ซ้อนกันของแบบฟอร์มที่d[ 'hello' ] = e e[ 'hi' ] = 'again'ในการสร้างพจนานุกรมที่ซ้อนกันฉันได้สร้างeพจนานุกรมเดียวและคัดลอกหลายครั้ง ฉันสังเกตเห็นว่าการใช้หน่วยความจำต่ำมากซึ่งทำให้เกิดคำถามของฉันที่นี่ ตอนนี้ฉันเข้าใจแล้วว่าไม่มีการสร้างสำเนาสตริงจึงใช้หน่วยความจำต่ำ
ปกติฉัน

1
หากคุณต้องการbเป็นเวอร์ชันแก้ไขaโดยไม่ต้องแก้ไขaเพียงแค่ให้bเป็นผลลัพธ์ของการดำเนินการใด ๆ เช่นb = a[2:-1]ชุดbไป'll'และaยังคง hello''
OJFord

Ollie ถูกต้อง เนื่องจาก str เป็นประเภทที่ไม่เปลี่ยนรูป เนื่องจากการใช้ singletons ของ python (และอาจเป็นการเพิ่มประสิทธิภาพภายในอื่น ๆ ) คุณจะไม่เห็นหน่วยความจำขยายอย่างที่คุณคาดหวังเมื่อคัดลอกพจนานุกรม e
FizxMike

คำตอบ:


137

คุณไม่จำเป็นต้องคัดลอกสตริง Python พวกมันไม่เปลี่ยนรูปและcopyโมดูลจะส่งคืนต้นฉบับเสมอในกรณีเช่น do str()คือสไลซ์สตริงทั้งหมดและต่อด้วยสตริงว่าง

ยิ่งไปกว่านั้น'hello'สตริงของคุณยังคงอยู่ ( บางสตริง ) Python พยายามเก็บสำเนาไว้เพียงชุดเดียวเนื่องจากจะทำให้การค้นหาพจนานุกรมเร็วขึ้น

วิธีหนึ่งที่คุณสามารถแก้ไขปัญหานี้ได้คือการสร้างสตริงใหม่จากนั้นตัดสตริงนั้นกลับไปที่เนื้อหาต้นฉบับ:

>>> a = 'hello'
>>> b = (a + '.')[:-1]
>>> id(a), id(b)
(4435312528, 4435312432)

แต่สิ่งที่คุณทำตอนนี้คือการสิ้นเปลืองหน่วยความจำ ไม่ใช่ว่าคุณสามารถกลายพันธุ์วัตถุสตริงเหล่านี้ได้ในทางใดทางหนึ่ง

หากสิ่งที่คุณต้องการทราบคือจำนวนหน่วยความจำที่วัตถุ Python ต้องการให้ใช้sys.getsizeof(); มันให้รอยเท้าหน่วยความจำของวัตถุ Python ใด ๆ

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

>>> import sys
>>> a = 'hello'
>>> sys.getsizeof(a)
42
>>> b = {'foo': 'bar'}
>>> sys.getsizeof(b)
280
>>> sys.getsizeof(b) + sum(sys.getsizeof(k) + sys.getsizeof(v) for k, v in b.items())
360

จากนั้นคุณสามารถเลือกใช้id()การติดตามเพื่อใช้รอยเท้าหน่วยความจำจริงหรือเพื่อประมาณรอยเท้าสูงสุดหากอ็อบเจ็กต์ไม่ได้ถูกแคชและใช้ซ้ำ


4
มีมากกว่าหนึ่งวิธีในการสร้างวัตถุสตริงใหม่เช่นb = ''.join(a).
martineau

@martineau: แน่นอนฉันตั้งใจจะพูด 'ทางเดียว' จริงๆ
Martijn Pieters

10
เน้นที่ "คุณไม่จำเป็นต้องคัดลอกสตริง Python" มีเหตุผลว่าทำไมการดำเนินการเหล่านั้นจึงส่งคืนสตริงเดียวกัน
tcooc

1
ในกรณีนี้ OP พยายามที่จะใช้หน่วยความจำอย่างสิ้นเปลือง เนื่องจากเขาต้องการรู้ว่าจะใช้หน่วยความจำมากแค่ไหนกับสตริงจำนวนหนึ่งนั่นคือเป้าหมายที่แท้จริง เห็นได้ชัดว่าเขาสามารถสร้างสตริงที่ไม่ซ้ำกันได้ แต่นั่นเป็นเพียงงานที่ไม่จำเป็นเพื่อเป็นวิธีแก้ปัญหา
Gabe

8
+1 สำหรับ "ลวก" โดยใช้ตัวอย่างที่จะส่งออก42
Bakuriu

11

คุณสามารถคัดลอกสตริงใน python ผ่านการจัดรูปแบบสตริง:

>>> a = 'foo'  
>>> b = '%s' % a  
>>> id(a), id(b)  
(140595444686784, 140595444726400)  

4
ไม่เป็นจริงใน Python 3.6.5 id (a) และ id (b) เหมือนกัน ผลลัพธ์ไม่แตกต่างกันแม้ว่าฉันจะใช้ฟอร์แมตเวอร์ชันใหม่ก็ตาม,b = '{:s}'.format(a)
Seshadri R

7

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

ตราบใดที่a, b, c, dและeมีค่าเดียวกันพวกเขาอ้างอิงถึงสถานที่เดียวกัน บันทึกหน่วยความจำแล้ว ทันทีที่ตัวแปรเริ่มมีค่าที่แตกต่างกันตัวแปรจะเริ่มมีการอ้างอิงที่แตกต่างกัน ประสบการณ์การเรียนรู้ของฉันมาจากรหัสนี้:

import copy
a = 'hello'
b = str(a)
c = a[:]
d = a + ''
e = copy.copy(a)

print map( id, [ a,b,c,d,e ] )

print a, b, c, d, e

e = a + 'something'
a = 'goodbye'
print map( id, [ a,b,c,d,e ] )
print a, b, c, d, e

ผลลัพธ์ที่พิมพ์ออกมาคือ:

[4538504992, 4538504992, 4538504992, 4538504992, 4538504992]

hello hello hello hello hello

[6113502048, 4538504992, 4538504992, 4538504992, 5570935808]

goodbye hello hello hello hello something

รายละเอียดเพิ่มเติมสำหรับพฤติกรรมอธิบายไว้ในโพสต์นี้stackoverflow.com/questions/2123925/…
dlasalle

3

การคัดลอกสตริงสามารถทำได้สองวิธีคือคัดลอกตำแหน่ง a = "a" b = a หรือคุณสามารถโคลนซึ่งหมายความว่า b จะไม่ได้รับผลกระทบเมื่อมีการเปลี่ยนแปลงซึ่งกระทำโดย a = 'a' b = a [:]


2

หากต้องการใช้วิธีอื่น "id ()" ไม่ใช่สิ่งที่คุณสนใจ คุณต้องการทราบว่าสามารถแก้ไขชื่อตัวแปรโดยไม่ทำร้ายชื่อตัวแปรต้นทางได้หรือไม่

>>> a = 'hello'                                                                                                                                                                                                                                                                                        
>>> b = a[:]                                                                                                                                                                                                                                                                                           
>>> c = a                                                                                                                                                                                                                                                                                              
>>> b += ' world'                                                                                                                                                                                                                                                                                      
>>> c += ', bye'                                                                                                                                                                                                                                                                                       
>>> a                                                                                                                                                                                                                                                                                                  
'hello'                                                                                                                                                                                                                                                                                                
>>> b                                                                                                                                                                                                                                                                                                  
'hello world'                                                                                                                                                                                                                                                                                          
>>> c                                                                                                                                                                                                                                                                                                  
'hello, bye'                                                                                                                                                                                                                                                                                           

หากคุณคุ้นเคยกับ C สิ่งเหล่านี้จะเหมือนกับตัวแปรตัวชี้ยกเว้นคุณไม่สามารถยกเลิกการอ้างอิงเพื่อแก้ไขสิ่งที่ชี้ไปได้ แต่ id () จะบอกคุณว่าขณะนี้ชี้ไปที่ใด

ปัญหาสำหรับโปรแกรมเมอร์ python เกิดขึ้นเมื่อคุณพิจารณาโครงสร้างที่ลึกกว่าเช่นรายการหรือคำสั่ง:

>>> o={'a': 10}                                                                                                                                                                                                                                                                                        
>>> x=o                                                                                                                                                                                                                                                                                                
>>> y=o.copy()                                                                                                                                                                                                                                                                                         
>>> x['a'] = 20                                                                                                                                                                                                                                                                                        
>>> y['a'] = 30                                                                                                                                                                                                                                                                                        
>>> o                                                                                                                                                                                                                                                                                                  
{'a': 20}                                                                                                                                                                                                                                                                                              
>>> x                                                                                                                                                                                                                                                                                                  
{'a': 20}                                                                                                                                                                                                                                                                                              
>>> y                                                                                                                                                                                                                                                                                                  
{'a': 30}                                                                                                                                                                                                                                                                                              

ที่นี่ o และ x อ้างถึงคำสั่งเดียวกัน o ['a'] และ x ['a'] และคำสั่งนั้น "ไม่แน่นอน" ในแง่ที่คุณสามารถเปลี่ยนค่าสำหรับคีย์ 'a' ได้ นั่นเป็นเหตุผลที่ "y" ต้องเป็นสำเนาและ y ['a'] สามารถอ้างถึงอย่างอื่นได้

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