Разве строки Python не являются неизменяемыми?Тогда почему + "" + b работает? - PullRequest
89 голосов
/ 01 февраля 2012

Насколько я понимаю, строки 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 не должен был помешать назначению? Я, наверное, что-то упустил.

Есть идеи?

Ответы [ 19 ]

156 голосов
/ 01 февраля 2012

Первый a указал на строку «Собака». Затем вы изменили переменную a, чтобы она указывала на новую строку «Собака ест угощение». Вы на самом деле не мутировали строку «Собака». Строки являются неизменяемыми, переменные могут указывать на то, что они хотят.

46 голосов
/ 01 февраля 2012

Сами строковые объекты являются неизменяемыми.

Переменная 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.
38 голосов
/ 01 февраля 2012

Переменная a указывает на объект «Собака». Лучше всего рассматривать переменную в Python как тег. Вы можете переместить тег на разные объекты, что вы и сделали, когда изменили a = "dog" на a = "dog eats treats".

Однако неизменность относится к объекту, а не к тегу.


Если вы попытаетесь a[1] = 'z' превратить "dog" в "dzg", вы получите ошибку:

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

потому что строки не поддерживают назначение элементов, поэтому они неизменны.

12 голосов
/ 27 июня 2015

Что-то изменчиво только тогда, когда мы можем изменить значения, хранящиеся в ячейке памяти, без изменения самой ячейки памяти.

Хитрость заключается в следующем: если вы обнаружите, что ячейка памяти до и после изменениято же самое, оно изменчиво.

Например, список изменчив.Как?

>> 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 голосов
/ 01 февраля 2012

Переменная - это просто метка, указывающая на объект.Объект является неизменным, но вы можете сделать метку указывающей на совершенно другой объект, если хотите.

7 голосов
/ 20 ноября 2016

Рассмотрим:

>>> 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 - это просто метка, указывающая на этот адрес памяти. Это может быть изменено, чтобы указать на что-либо.

6 голосов
/ 01 февраля 2012

Выражение 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", что означает неизменный. Однако мы можем изменить метки, если таковые имеются, которые указывают на этот раздел памяти.

6 голосов
/ 01 февраля 2012
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 голосов
/ 01 февраля 2012

Существует разница между данными и меткой, с которой они связаны.Например, когда вы делаете

a = "dog"

, данные "dog" создаются и помещаются под меткой a.Метка может измениться, но то, что в памяти, не будетДанные "dog" все еще будут существовать в памяти (пока сборщик мусора не удалит их) после того, как вы выполните

a = "cat"

В вашей программе a сейчас ^ указывает на ^ "cat", но на строку "dog"не изменился.

3 голосов
/ 01 февраля 2012

Строки Python являются неизменяемыми. Однако a не является строкой: это переменная со строковым значением. Вы не можете изменить строку, но можете изменить значение переменной на новую строку.

...