สตริง Python ไม่เปลี่ยนรูปหรือไม่? แล้วทำไม a +“” + b ถึงใช้งานได้?


110

ความเข้าใจของฉันคือสตริง Python ไม่เปลี่ยนรูป

ฉันลองใช้รหัสต่อไปนี้:

a = "Dog"
b = "eats"
c = "treats"

print a, b, c
# Dog eats treats

print a + " " + b + " " + c
# Dog eats treats

print a
# Dog

a = a + " " + b + " " + c
print a
# Dog eats treats
# !!!

Python ไม่ควรป้องกันการมอบหมายหรือไม่ ฉันคงขาดอะไรไป

ความคิดใด ๆ ?


55
สายอักขระไม่เปลี่ยนรูป แต่ป้ายกำกับสามารถเปลี่ยนแปลงได้
mitch

6
การกำหนดค่าใหม่ให้กับตัวแปรที่มีอยู่นั้นใช้ได้อย่างสมบูรณ์ Python ไม่มีค่าคงที่ สิ่งนี้เป็นอิสระจากความไม่แน่นอนของชนิดข้อมูล
Felix Kling

14
คุณอาจต้องการดูid()ฟังก์ชัน aจะมี id ที่แตกต่างกันก่อนและหลังการกำหนดซึ่งบ่งชี้ว่ากำลังชี้ไปที่วัตถุที่แตกต่างกัน ในทำนองเดียวกันกับรหัสเช่นb = aคุณจะพบว่าaและbจะมีรหัสเดียวกันแสดงว่าพวกเขาอ้างอิงวัตถุเดียวกัน
DRH


ลิงก์จากเดลแนนคือสิ่งที่ฉันอ้างถึง
mitch

คำตอบ:


182

อันดับแรกaชี้ไปที่สตริง "Dog" จากนั้นคุณเปลี่ยนตัวแปรaให้ชี้ไปที่สตริงใหม่ "Dog กินขนม" คุณไม่ได้กลายพันธุ์สตริง "Dog" จริงๆ สตริงไม่เปลี่ยนรูปตัวแปรสามารถชี้ไปที่สิ่งที่ต้องการได้


34
มันน่าเชื่อกว่าที่จะลองทำอะไรเช่น x = 'abc'; x [1] = 'x' ในการจำลอง Python
xpmatteo

1
หากคุณต้องการทำความเข้าใจภายในเพิ่มเติมอีกเล็กน้อยโปรดดูคำตอบของฉัน stackoverflow.com/a/40702094/117471
Bruno Bronosky

54

สตริงวัตถุนั้นไม่เปลี่ยนรูป

ตัวแปรaซึ่งชี้ไปที่สตริงนั้นไม่แน่นอน

พิจารณา:

a = "Foo"
# a now points to "Foo"
b = a
# b points to the same "Foo" that a points to
a = a + a
# a points to the new string "FooFoo", but b still points to the old "Foo"

print a
print b
# Outputs:

# FooFoo
# Foo

# Observe that b hasn't changed, even though a has.

@jason ลองใช้การดำเนินการแบบเดียวกันกับรายการ (ที่ไม่แน่นอน) เพื่อดูความแตกต่าง a.append (3) สอดคล้องกับ a = a + "Foo"
jimifiki

1
@jimifiki a.append(3) ไม่เหมือนกับa = a + 3. มันไม่เท่ากันa += 3(การเติมเข้าแทนที่เทียบเท่ากับ.extendไม่ใช่.append)

@ เดลแนนแล้วไง เพื่อประโยชน์ในการแสดงให้เห็นว่าสตริงและรายการทำงานแตกต่างกันคุณสามารถสันนิษฐานได้ว่า a = a + "Foo" เหมือนกับ a.append (บางอย่าง) ไม่ว่าในกรณีใดก็ไม่เหมือนกัน เห็นได้ชัด. คุณมีความสุขมากขึ้นที่ได้อ่าน a.extend ([บางอย่าง]) แทน a.append (บางอย่าง) หรือไม่? ฉันไม่เห็นความแตกต่างที่ยิ่งใหญ่ในบริบทนี้ แต่ฉันอาจจะขาดอะไรไป Thruth ขึ้นอยู่กับบริบท
jimifiki

@jimifiki: คุณกำลังพูดถึงอะไร? +ทำงานเหมือนกันสำหรับรายการและสตริง - มันเชื่อมต่อกันโดยสร้างสำเนาใหม่และไม่กลายพันธุ์ตัวถูกดำเนินการอย่างใดอย่างหนึ่ง

6
จุดสำคัญอย่างแท้จริงที่จะนำออกไปจากทั้งหมดนี้คือสตริงไม่มี append ฟังก์ชันเนื่องจากไม่เปลี่ยนรูป
Lily Chung

46

ตัวแปร a ชี้ไปที่วัตถุ "Dog" ที่ดีที่สุดคือคิดว่าตัวแปรใน Python เป็นแท็ก คุณสามารถย้ายแท็กไปยังวัตถุที่แตกต่างกันซึ่งเป็นสิ่งที่คุณได้เมื่อคุณเปลี่ยนไปa = "dog"a = "dog eats treats"

อย่างไรก็ตามความไม่เปลี่ยนรูปหมายถึงวัตถุไม่ใช่แท็ก


ถ้าคุณพยายามa[1] = 'z'ที่จะทำให้การ"dog"เข้ามา"dzg"คุณจะได้รับข้อผิดพลาด:

TypeError: 'str' object does not support item assignment" 

เนื่องจากสตริงไม่รองรับการกำหนดรายการจึงไม่เปลี่ยนรูป


19

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

เคล็ดลับคือ: หากคุณพบว่าตำแหน่งหน่วยความจำก่อนและหลังการเปลี่ยนแปลงเหมือนกันแสดงว่าไม่แน่นอน

ตัวอย่างเช่นรายการไม่แน่นอน อย่างไร?

>> a = ['hello']
>> id(a)
139767295067632

# Now let's modify
#1
>> a[0] = "hello new"
>> a
['hello new']
Now that we have changed "a", let's see the location of a
>> id(a)
139767295067632
so it is the same as before. So we mutated a. So list is mutable.

สตริงไม่เปลี่ยนรูป เราจะพิสูจน์ได้อย่างไร?

> a = "hello"
> a[0]
'h'
# Now let's modify it
> a[0] = 'n'
----------------------------------------------------------------------

เราได้รับ

TypeError: วัตถุ 'str' ไม่รองรับการกำหนดรายการ

ดังนั้นเราจึงล้มเหลวในการกลายพันธุ์สตริง หมายความว่าสตริงไม่เปลี่ยนรูป

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

>> a = "hello"
>> id(a)
139767308749440
>> a ="world"
>> id(a)
139767293625808

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


11

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


8

พิจารณา:

>>> a='asdf'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x1091aab90>
>>> a='asdf'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x1091aab90>
>>> a='qwer'
>>> a.__repr__
<method-wrapper '__repr__' of str object at 0x109198490>

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


7

คำสั่งa = a + " " + b + " " + cสามารถแยกย่อยตามคำแนะนำ

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

หน่วยความจำ:

working_set = "Dog "
a = "Dog" 
b = "eats"
c = "treats"

+ bบอกว่าให้คะแนนอะไรบ้างที่bเปลี่ยนแปลงไม่ได้และเพิ่มลงในชุดการทำงานปัจจุบัน

หน่วยความจำ:

working_set = "Dog eats"
a = "Dog" 
b = "eats"
c = "treats"

+ " " + cกล่าวว่าเพิ่ม" "ในชุดปัจจุบัน จากนั้นให้คะแนนอะไรบ้างที่cเปลี่ยนแปลงไม่ได้แล้วเพิ่มลงในชุดการทำงานปัจจุบัน หน่วยความจำ:

working_set = "Dog eats treats"
a = "Dog" 
b = "eats"
c = "treats"

สุดท้ายa =ตั้งค่าตัวชี้ของฉันให้ชี้ไปที่ชุดผลลัพธ์

หน่วยความจำ:

a = "Dog eats treats"
b = "eats"
c = "treats"

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



5

มีความแตกต่างระหว่างข้อมูลและป้ายกำกับที่เกี่ยวข้อง ตัวอย่างเช่นเมื่อคุณทำ

a = "dog"

ข้อมูลจะถูกสร้างขึ้นและนำภายใต้ชื่อ"dog" aป้ายกำกับสามารถเปลี่ยนแปลงได้ แต่สิ่งที่อยู่ในหน่วยความจำจะไม่เปลี่ยน ข้อมูล"dog"จะยังคงอยู่ในหน่วยความจำ (จนกว่าตัวรวบรวมขยะจะลบทิ้ง) หลังจากที่คุณทำ

a = "cat"

ในโปรแกรมของคุณaตอนนี้ ^ ชี้ไปที่ ^ "cat"แต่สตริง"dog"ไม่เปลี่ยนแปลง


3

สตริง Python ไม่เปลี่ยนรูป อย่างไรก็ตามaไม่ใช่สตริง แต่เป็นตัวแปรที่มีค่าสตริง คุณไม่สามารถเปลี่ยนสายอักขระ แต่สามารถเปลี่ยนค่าของตัวแปรเป็นสตริงใหม่ได้


2

ตัวแปรสามารถชี้ไปที่ใดก็ได้ที่ต้องการ .. จะมีข้อผิดพลาดเกิดขึ้นหากคุณทำสิ่งต่อไปนี้:

a = "dog"
print a                   #dog
a[1] = "g"                #ERROR!!!!!! STRINGS ARE IMMUTABLE

2

วัตถุสตริง Python ไม่เปลี่ยนรูป ตัวอย่าง:

>>> a = 'tanim'
>>> 'Address of a is:{}'.format(id(a))
'Address of a is:64281536'
>>> a = 'ahmed'
>>> 'Address of a is:{}'.format(id(a))
'Address of a is:64281600'

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

  >>> a[0] = 'c'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    **TypeError**: 'str' object does not support item assignment

เกิดข้อผิดพลาด


2

'mutable' หมายความว่าเราสามารถเปลี่ยนเนื้อหาของสตริงได้ 'ไม่เปลี่ยนรูป' หมายความว่าเราไม่สามารถเพิ่มสตริงพิเศษได้

คลิกเพื่อพิสูจน์ภาพถ่าย


1

>>> a = 'dogs'

>>> a.replace('dogs', 'dogs eat treats')

'dogs eat treats'

>>> print a

'dogs'

ไม่เปลี่ยนรูปไม่ใช่เหรอ!

ในส่วนของการเปลี่ยนแปลงตัวแปรได้กล่าวถึงไปแล้ว


1
นี่ไม่ได้เป็นการพิสูจน์หรือพิสูจน์ความไม่แน่นอนของสตริง python แต่replace()วิธีนี้จะส่งคืนสตริงใหม่
Brent Hronik

1

ลองพิจารณาสิ่งนี้เพิ่มเติมจากตัวอย่างของคุณ

 a = "Dog"
 b = "eats"
 c = "treats"
 print (a,b,c)
 #Dog eats treats
 d = a + " " + b + " " + c
 print (a)
 #Dog
 print (d)
 #Dog eats treats

คำอธิบายที่แม่นยำยิ่งขึ้นอย่างหนึ่งที่ฉันพบในบล็อกคือ:

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

เช่น:

some_guy = 'Fred'
# ...
some_guy = 'George'

เมื่อเราพูดว่า some_guy = 'George' ในภายหลังวัตถุสตริงที่มี 'Fred' จะไม่ได้รับผลกระทบ เราเพิ่งเปลี่ยนการผูกชื่อ some_guy อย่างไรก็ตามเรายังไม่ได้เปลี่ยนอ็อบเจ็กต์สตริง "Fred" หรือ "George" เท่าที่เรากังวลพวกเขาอาจมีชีวิตอยู่ไปเรื่อย ๆ

ลิงก์ไปยังบล็อก: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/


1

เพิ่มอีกเล็กน้อยสำหรับคำตอบที่กล่าวถึงข้างต้น

id ของการเปลี่ยนแปลงตัวแปรเมื่อกำหนดใหม่

>>> a = 'initial_string'
>>> id(a)
139982120425648
>>> a = 'new_string'
>>> id(a)
139982120425776

ซึ่งหมายความว่าเราได้กลายพันธุ์ตัวแปรaเพื่อชี้ไปที่สตริงใหม่ ตอนนี้มีอยู่สอง stringวัตถุ (str):

'initial_string'กับid= 139982120425648

และ

'new_string'กับid= 139982120425776

พิจารณารหัสด้านล่าง:

>>> b = 'intitial_string'
>>> id(b)
139982120425648

ตอนนี้bชี้ไปที่'initial_string'และเหมือนกับidที่aมีก่อนการมอบหมายใหม่

ดังนั้นจึง'intial_string'ไม่กลายพันธุ์


0

สรุป:

a = 3
b = a
a = 3+2
print b
# 5

ไม่เปลี่ยนรูป:

a = 'OOP'
b = a
a = 'p'+a
print b
# OOP

ไม่เปลี่ยนรูป:

a = [1,2,3]
b = range(len(a))
for i in range(len(a)):
    b[i] = a[i]+1

นี่เป็นข้อผิดพลาดใน Python 3 เนื่องจากไม่เปลี่ยนรูป และไม่ใช่ข้อผิดพลาดใน Python 2 เพราะเห็นได้ชัดว่ามันไม่เปลี่ยนรูป


0

ฟังก์ชันid()ในตัวจะส่งคืนข้อมูลประจำตัวของวัตถุเป็นจำนวนเต็ม จำนวนเต็มนี้มักจะตรงกับตำแหน่งของวัตถุในหน่วยความจำ

\>>a='dog'
\>>print(id(a))

139831803293008

\>>a=a+'cat'
\>>print(id(a))

139831803293120

ในขั้นต้น 'a' จะถูกเก็บไว้ในตำแหน่งหน่วยความจำ 139831803293008 เนื่องจากวัตถุสตริงไม่เปลี่ยนรูปใน python หากคุณพยายามแก้ไขและกำหนดการอ้างอิงใหม่จะถูกลบออกและจะเป็นตัวชี้ไปยังตำแหน่งหน่วยความจำใหม่ (139831803293120)


0
a = 'dog'
address = id(a)
print(id(a))

a = a + 'cat'
print(id(a))      #Address changes

import ctypes
ctypes.cast(address, ctypes.py_object).value    #value at old address is intact

2
แม้ว่ารหัสนี้อาจช่วยแก้ปัญหาของ OP ได้ แต่ทางที่ดีควรใส่คำอธิบายว่ารหัสของคุณจัดการกับปัญหาของ OP อย่างไร ด้วยวิธีนี้ผู้เข้าชมในอนาคตสามารถเรียนรู้จากโพสต์ของคุณและนำไปใช้กับโค้ดของตนเองได้ SO ไม่ใช่บริการเข้ารหัส แต่เป็นแหล่งข้อมูลสำหรับความรู้ นอกจากนี้คำตอบที่มีคุณภาพสูงและครบถ้วนก็มีแนวโน้มที่จะได้รับการโหวตมากขึ้น คุณสมบัติเหล่านี้พร้อมกับข้อกำหนดที่ว่าโพสต์ทั้งหมดเป็นแบบในตัวเองเป็นจุดแข็งบางประการของ SO ในฐานะแพลตฟอร์มที่ทำให้มันแตกต่างจากฟอรัม คุณสามารถแก้ไขเพื่อเพิ่มข้อมูลเพิ่มเติม & / หรือเพื่อเสริมคำอธิบายของคุณด้วยเอกสารที่มา
SherylHohman


-1

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

ความสับสนคือเราเป็นตัวแทนของบล็อกหน่วยความจำ (ที่มีข้อมูลหรือข้อมูล) ในแบ็กเอนด์ด้วยชื่อตัวแปรเดียวกัน


-2

คุณสามารถทำให้อาร์เรย์ numpy ไม่เปลี่ยนรูปและใช้องค์ประกอบแรก:

numpyarrayname[0] = "write once"

แล้ว:

numpyarrayname.setflags(write=False)

หรือ

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