Разве строки Python не неизменяемы? Тогда почему работает a + «» + b?


111

Насколько я понимаю, строки 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 нет констант. Это не зависит от изменчивости типа данных.
Феликс Клинг,

14
Вы можете взглянуть на id()функцию. aбудет иметь другой идентификатор до и после назначения, что указывает на то, что он указывает на разные объекты. Точно так же с кодом, который b = aвы обнаружите, aи bбудет иметь тот же идентификатор, что означает, что они ссылаются на один и тот же объект.
DRH


Ссылка от delnan - это именно то, о чем я имел в виду.
mitch

Ответы:


182

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


34
Еще более убедительно попробовать что-то вроде x = 'abc'; x [1] = 'x' в Python repl
xpmatteo

1
Если вы хотите немного разобраться во внутреннем устройстве, посмотрите мой ответ. stackoverflow.com/a/40702094/117471
Бруно Броноски, 09

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 01

1
@jimifiki a.append(3) - это не то же самое, что a = a + 3. Это даже не a += 3(дополнение на месте эквивалентно .extend, а не .append).

@delnan и так что? Чтобы показать, что строки и списки ведут себя по-разному, вы можете предположить, что a = a + "Foo" то же самое, что a.append (что-то). В любом случае это не то же самое. Очевидно. Вам больше понравилось читать a.extend ([что-то]) вместо a.append (что-то)? Я не вижу такой большой разницы в этом контексте. Но наверное я чего-то упускаю Правда зависит от контекста
jimifiki 01

@jimifiki: О чем ты говоришь? +ведет себя одинаково для списков и строк - он объединяется путем создания новой копии и не изменяет ни один из операндов.

6
По-настоящему важный момент, который следует извлечь из всего этого, - это то, что у струн нет append функции, потому что они неизменяемы.
Лили Чанг

46

Переменная a указывает на объект «Собака». Лучше думать о переменной в 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"в котором находился, что и подразумевается под неизменным. Однако мы можем изменить, какие метки, если они есть, указывают на этот раздел памяти.


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

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

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

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

нажмите, чтобы увидеть фото


1

>>> a = 'dogs'

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

'dogs eat treats'

>>> print a

'dogs'

Неизменный, не правда ли ?!

Часть изменения переменной уже обсуждалась.


1
Это не доказывает и не опровергает изменчивость строк Python, просто replace()метод возвращает новую строку.
Брент Хроник

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. Однако мы не изменили ни строковые объекты «Фред», ни «Джордж». Насколько нам известно, они могут жить бесконечно.

Ссылка на блог: 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 как платформы, которые отличает ее от форумов. Вы можете редактировать, чтобы добавить дополнительную информацию и / или дополнить свои пояснения исходной документацией
Шерил Хохман,


-1

Мы просто объединяем два строковых значения. Мы никогда не меняем значение (а). Только что (а) представляют другой блок памяти, который имеет значение "dogdog". Потому что в бэкэнде одна переменная никогда не представляет два блока памяти одновременно. Значение (а) перед конкатенацией было «собака». Но после этого (а) представляют "собаку", потому что теперь (а) в backend rep. блок со значением "dogdog". А «собака» - это респ. by (b) и «собака» не считается значением мусора, пока (b) не представляет «собаку».

Путаница в том, что мы представляем блоки памяти (содержащие данные или информацию) в бэкэнде с тем же именем переменной.


-2

Вы можете сделать массив numpy неизменяемым и использовать первый элемент:

numpyarrayname[0] = "write once"

затем:

numpyarrayname.setflags(write=False)

или

numpyarrayname.flags.writeable = False
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.