Không phải là chuỗi Python bất biến? Vậy tại sao a + “” + b hoạt động?


109

Sự hiểu biết của tôi là các chuỗi Python là bất biến.

Tôi đã thử mã sau:

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 không nên ngăn chặn việc chuyển nhượng? Tôi có lẽ đang thiếu một cái gì đó.

Bất kỳ ý tưởng?


55
Bản thân chuỗi là bất biến nhưng nhãn có thể thay đổi.
mitch

6
Việc gán giá trị mới cho một biến hiện có là hoàn toàn hợp lệ. Python không có hằng số. Điều này độc lập với khả năng thay đổi kiểu dữ liệu.
Felix Kling

14
Bạn có thể muốn xem qua id()chức năng. asẽ có một id khác trước và sau khi gán, cho biết rằng nó đang trỏ đến các đối tượng khác nhau. Tương tự như vậy với mã như vậy, b = abạn sẽ tìm thấy absẽ có cùng một id, cho thấy chúng đang tham chiếu đến cùng một đối tượng.
DRH


Liên kết từ delnan chính xác là những gì tôi đang tham khảo.
mitch

Câu trả lời:


180

Đầu tiên achỉ vào chuỗi "Dog". Sau đó, bạn đã thay đổi biến ađể trỏ vào một chuỗi mới "Con chó ăn món". Bạn không thực sự biến đổi chuỗi "Dog". Các chuỗi là bất biến, các biến có thể trỏ đến bất cứ thứ gì chúng muốn.


34
Còn thuyết phục hơn khi thử một cái gì đó như x = 'abc'; x [1] = 'x' trong Python repl
xpmatteo

1
Nếu bạn muốn hiểu bên trong hơn một chút, hãy xem câu trả lời của tôi. stackoverflow.com/a/40702094/117471
Bruno Bronosky,

53

Bản thân các đối tượng chuỗi là bất biến.

Biến, atrỏ đến chuỗi, có thể thay đổi.

Xem xét:

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 hãy thử cùng một loại thao tác với danh sách (có thể thay đổi) để xem sự khác biệt a.append (3) tương ứng với a = a + "Foo"
jimifiki

1
@jimifiki a.append(3) không giống như a = a + 3. Nó thậm chí không a += 3(bổ sung thay thế tương đương với .extend, không phải .append).

@delnan và như vậy, cái gì? Để hiển thị rằng các chuỗi và danh sách hoạt động khác nhau, bạn có thể giả định rằng a = a + "Foo" giống với a.append (cái gì đó). Trong mọi trường hợp, nó không giống nhau. Chắc chắn. Bạn có thấy vui hơn khi đọc a.extend ([something]) thay vì a.append (something) không? Tôi không thấy sự khác biệt lớn đó trong bối cảnh này. Nhưng có lẽ tôi đang thiếu một cái gì đó. Thruth phụ thuộc vào bối cảnh
jimifiki

@jimifiki: Bạn đang nói về cái gì vậy? +hoạt động giống nhau đối với danh sách và chuỗi - nó nối bằng cách tạo một bản sao mới và không làm thay đổi toán hạng.

6
Điểm thực sự quan trọng cần loại bỏ tất cả những điều này là các chuỗi không có append chức năng vì chúng bất biến.
Lily Chung

46

Biến a đang trỏ vào đối tượng "Dog". Tốt nhất hãy nghĩ về biến trong Python như một thẻ. Bạn có thể di chuyển thẻ sang các đối tượng khác nhau, đó là những gì bạn đã làm khi chuyển a = "dog"sang a = "dog eats treats".

Tuy nhiên, tính bất biến đề cập đến đối tượng, không phải thẻ.


Nếu bạn đã cố gắng a[1] = 'z'để làm cho "dog"thành "dzg", bạn sẽ nhận được lỗi:

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

bởi vì các chuỗi không hỗ trợ gán mục, do đó chúng là bất biến.


19

Một cái gì đó chỉ có thể thay đổi khi chúng ta có thể thay đổi các giá trị được giữ trong vị trí bộ nhớ mà không thay đổi chính vị trí bộ nhớ.

Bí quyết là: Nếu bạn thấy rằng vị trí bộ nhớ trước và sau khi thay đổi giống nhau, nó có thể thay đổi được.

Ví dụ: danh sách có thể thay đổi. Làm sao?

>> 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.

Một chuỗi là bất biến. Làm thế nào để chúng tôi chứng minh nó?

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

chúng tôi nhận được

TypeError: Đối tượng 'str' không hỗ trợ gán mục

Vì vậy, chúng tôi không thể thay đổi chuỗi. Nó có nghĩa là một chuỗi là bất biến.

Khi bạn gán lại, bạn thay đổi biến để trỏ đến một vị trí mới. Ở đây bạn không biến đổi chuỗi mà là biến đổi chính biến. Sau đây là những gì bạn đang làm.

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

idtrước và sau khi gán lại là khác nhau, vì vậy điều này chứng tỏ rằng bạn thực sự không đột biến, mà đang trỏ biến đến vị trí mới. Cái nào không phải là đột biến chuỗi đó, mà là thay đổi biến đó.


11

Một biến chỉ là một nhãn trỏ đến một đối tượng. Đối tượng là bất biến, nhưng bạn có thể làm cho nhãn trỏ đến một đối tượng hoàn toàn khác nếu bạn muốn.


8

Xem xét:

>>> 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>

Lưu ý rằng vị trí bộ nhớ hex không thay đổi khi tôi lưu cùng một giá trị trong biến hai lần. Nó đã thay đổi khi tôi lưu trữ một giá trị khác. Chuỗi là bất biến. Không phải vì quá sốt sắng, mà vì bạn phải trả tiền phạt khi tạo một đối tượng mới trong bộ nhớ. Biến achỉ là một nhãn trỏ đến địa chỉ bộ nhớ đó. Nó có thể được thay đổi để trỏ đến bất cứ thứ gì.


7

Câu lệnh a = a + " " + b + " " + ccó thể được chia nhỏ dựa trên các con trỏ.

a + " "nói cho tôi những ađiểm nào, điểm nào không thể thay đổi và thêm vào" " vào bộ làm việc hiện tại của tôi.

ký ức:

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

+ bnói cho tôi những bđiểm nào, điểm nào không thể thay đổi và thêm nó vào bộ làm việc hiện tại.

ký ức:

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

+ " " + cnói thêm " "vào tập hợp hiện tại. Sau đó, cung cấp cho tôi những cđiểm không thể thay đổi và thêm nó vào bộ làm việc hiện tại. ký ức:

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

Cuối cùng, a = nói đặt con trỏ của tôi trỏ đến tập hợp kết quả.

ký ức:

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

"Dog"được lấy lại, bởi vì không còn con trỏ nào kết nối với phần bộ nhớ của nó. Chúng tôi không bao giờ sửa đổi phần bộ nhớ nằm "Dog"trong đó, đó là ý nghĩa của bất biến. Tuy nhiên, chúng ta có thể thay đổi các nhãn nào, nếu có, trỏ đến phần bộ nhớ đó.


6
l = [1,2,3]
print id(l)
l.append(4)
print id(l) #object l is the same

a = "dog"
print id(a)
a = "cat"
print id(a) #object a is a new object, previous one is deleted

5

Có sự khác biệt giữa dữ liệu và nhãn mà nó được liên kết. Ví dụ khi bạn làm

a = "dog"

dữ liệu "dog"được tạo và đặt dưới nhãn a. Nhãn có thể thay đổi nhưng những gì trong bộ nhớ thì không. Dữ liệu "dog"sẽ vẫn tồn tại trong bộ nhớ (cho đến khi bộ thu gom rác xóa nó) sau khi bạn

a = "cat"

Trong chương trình của bạn abây giờ ^ trỏ đến ^ "cat"nhưng chuỗi "dog"không thay đổi.


3

Chuỗi Python là bất biến. Tuy nhiên, anó không phải là một chuỗi: nó là một biến có giá trị chuỗi. Bạn không thể thay đổi chuỗi, nhưng có thể thay đổi giá trị của biến thành một chuỗi mới.


2

Các biến có thể trỏ đến bất kỳ nơi nào chúng muốn .. Sẽ xảy ra lỗi nếu bạn làm như sau:

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

2

Các đối tượng chuỗi Python là bất biến. Thí dụ:

>>> 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'

Trong ví dụ này, chúng ta có thể thấy rằng khi chúng ta gán giá trị khác nhau trong một giá trị, nó không sửa đổi. Một đối tượng mới được tạo ra.
Và nó không thể được sửa đổi. Thí dụ:

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

Một lỗi xảy ra.


2

'có thể thay đổi' có nghĩa là chúng ta có thể thay đổi nội dung của chuỗi, 'không thể thay đổi' có nghĩa là chúng ta không thể thêm một chuỗi bổ sung.

bấm vào để chứng minh ảnh


1

>>> a = 'dogs'

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

'dogs eat treats'

>>> print a

'dogs'

Bất biến, phải không ?!

Phần thay đổi biến đã được thảo luận.


1
Điều này không chứng minh hoặc bác bỏ khả năng thay đổi của chuỗi python, chỉ là replace()phương thức trả về một chuỗi mới.
Brent Hronik

1

Hãy xem xét bổ sung này cho ví dụ của bạn

 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

Một trong những lời giải thích chính xác hơn mà tôi tìm thấy trong blog là:

Trong Python, (hầu hết) mọi thứ đều là một đối tượng. Những gì chúng ta thường gọi là "biến" trong Python được gọi đúng hơn là tên. Tương tự như vậy, "gán" thực sự là ràng buộc của một tên với một đối tượng. Mỗi ràng buộc có một phạm vi xác định khả năng hiển thị của nó, thường là khối mà tên bắt nguồn.

Ví dụ:

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

Sau này khi chúng ta nói some_guy = 'George', đối tượng chuỗi có chứa 'Fred' sẽ không bị ảnh hưởng. Chúng tôi vừa thay đổi ràng buộc của tên some_guy. Tuy nhiên, chúng tôi đã không thay đổi đối tượng chuỗi 'Fred' hoặc 'George'. Theo như chúng tôi được biết, chúng có thể sống vô thời hạn.

Liên kết tới blog: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/


1

Thêm một chút nữa cho các câu trả lời nêu trên.

id của một biến thay đổi khi chuyển nhượng lại.

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

Có nghĩa là chúng ta đã biến đổi biến ađể trỏ đến một chuỗi mới. Bây giờ tồn tại hai string đối tượng (str):

'initial_string' với id = 139982120425648

'new_string'với id= 139982120425776

Hãy xem xét đoạn mã dưới đây:

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

Bây giờ, btrỏ đến 'initial_string'và có giống idnhư atrước khi phân công lại.

Vì vậy, 'intial_string'đã không bị đột biến.


0

Tổng kết:

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

Không thay đổi:

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

Bất biến:

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

Đây là một lỗi trong Python 3 vì nó là bất biến. Và không phải là một lỗi trong Python 2 vì rõ ràng nó không phải là bất biến.


0

Hàm dựng sẵn id()trả về danh tính của một đối tượng dưới dạng số nguyên. Số nguyên này thường tương ứng với vị trí của đối tượng trong bộ nhớ.

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

139831803293008

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

139831803293120

Ban đầu, 'a' được lưu trữ trong vị trí bộ nhớ 139831803293008, vì đối tượng chuỗi là bất biến trong python nếu bạn cố gắng sửa đổi và gán lại tham chiếu sẽ bị xóa và sẽ là một con trỏ đến vị trí bộ nhớ mới (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
Mặc dù mã này có thể giải quyết vấn đề của OP, nhưng tốt nhất là bạn nên kèm theo lời giải thích về cách mã của bạn giải quyết vấn đề của OP. Bằng cách này, khách truy cập trong tương lai có thể học từ bài đăng của bạn và áp dụng nó vào mã của riêng họ. SO không phải là một dịch vụ mã hóa, mà là một nguồn kiến ​​thức. Ngoài ra, các câu trả lời chất lượng cao, đầy đủ có nhiều khả năng được ủng hộ hơn. Những tính năng này, cùng với yêu cầu rằng tất cả các bài đăng đều phải khép kín, là một số điểm mạnh của SO với tư cách là một nền tảng, giúp phân biệt nó với các diễn đàn. Bạn có thể chỉnh sửa để thêm thông tin bổ sung & / hoặc để bổ sung giải thích của mình bằng tài liệu nguồn
SherylHohman


-1

Chúng tôi chỉ nối hai giá trị chuỗi. Chúng tôi không bao giờ thay đổi giá trị của (a). Vừa rồi (a) đại diện cho một khối bộ nhớ khác có giá trị "dogdog". Bởi vì trong backend, một biến không bao giờ đại diện cho hai khối bộ nhớ cùng một lúc. Giá trị của (a) trước khi nối là "dog". Nhưng sau đó (a) đại diện cho "dogdog", vì bây giờ (a) trong đại diện phụ trợ. khối có giá trị "dogdog". Và "con chó" là đại diện. bởi (b) và "dog" không được tính là giá trị rác cho đến khi (b) đại diện cho "dog".

Sự nhầm lẫn là chúng tôi đại diện cho các khối bộ nhớ (chứa dữ liệu hoặc thông tin.) Trong phần phụ trợ với cùng một tên biến.


-2

Bạn có thể làm cho một mảng numpy trở nên bất biến và sử dụng phần tử đầu tiên:

numpyarrayname[0] = "write once"

sau đó:

numpyarrayname.setflags(write=False)

hoặc là

numpyarrayname.flags.writeable = False
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.