Czy ciągi znaków Pythona nie są niezmienne? Więc dlaczego działa + „” + b?

110

Zrozumiałem, że ciągi znaków w Pythonie są niezmienne.

Wypróbowałem następujący kod:

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

Czy Python nie powinien był uniemożliwić przypisania? Prawdopodobnie czegoś mi brakuje.

Dowolny pomysł?

Jason
źródło
55
Sam ciąg jest niezmienny, ale etykieta może się zmienić.
mitch
6
Przypisanie nowej wartości do istniejącej zmiennej jest całkowicie poprawne. Python nie ma stałych. Jest to niezależne od zmienności typu danych.
Felix Kling
14
Możesz rzucić okiem na id()funkcję. abędzie miał inny identyfikator przed i po przypisaniu, co oznacza, że ​​wskazuje na różne obiekty. Podobnie w przypadku takiego kodu, b = aktóry znajdziesz ai bbędzie miał ten sam identyfikator, wskazujący, że odwołują się do tego samego obiektu.
DRH
Link z delnan jest dokładnie tym, o czym mówiłem.
mitch

Odpowiedzi:

182

Najpierw awskazał na napis „Pies”. Następnie zmieniłeś zmienną, aaby wskazywała na nowy ciąg znaków „Pies zjada smakołyki”. W rzeczywistości nie zmutowałeś ciągu „Pies”. Ciągi znaków są niezmienne, zmienne mogą wskazywać na cokolwiek chcą.

Bort
źródło
34
Jeszcze bardziej przekonujące jest wypróbowanie czegoś takiego jak x = 'abc'; x [1] = 'x' w
replice
1
Jeśli chcesz lepiej zrozumieć wewnętrzne elementy, zobacz moją odpowiedź. stackoverflow.com/a/40702094/117471
Bruno Bronosky
54

Same obiekty string są niezmienne.

Zmienna awskazująca na łańcuch jest modyfikowalna.

Rozważ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.
Sebastian Paaske Tørholm
źródło
@jason spróbuj tego samego rodzaju operacji z listami (które są zmienne), aby zobaczyć różnicę a.append (3) odpowiada a = a + "Foo"
jimifiki
1
@jimifiki a.append(3) to nie to samo co a = a + 3. Nie jest równe a += 3(dodawanie w miejscu jest równoważne z .extend, a nie z .append).
@delnan i co z tego? Aby pokazać, że łańcuchy i listy zachowują się inaczej, możesz założyć, że a = a + "Foo" to to samo, co a.append (coś). W każdym razie to nie to samo. Oczywiście. Czy byłeś szczęśliwszy czytając a.extend ([coś]) zamiast a.append (coś)? Nie widzę tak dużej różnicy w tym kontekście. Ale prawdopodobnie czegoś mi brakuje. Prawda zależy od kontekstu
jimifiki
@jimifiki: O czym ty mówisz? +zachowuje się tak samo dla list i ciągów - łączy się, tworząc nową kopię i nie modyfikując żadnego operandu.
6
Naprawdę ważną kwestią, którą należy wyciągnąć z tego wszystkiego, jest to, że łańcuchy nie mają append funkcji, ponieważ są niezmienne.
Lily Chung
46

Zmienna a wskazuje na obiekt „Pies”. Najlepiej jest myśleć o zmiennej w Pythonie jako o tagu. Możesz przenieść znacznik do różnych obiektów, tak jak zrobiłeś to po zmianie a = "dog"na a = "dog eats treats".

Jednak niezmienność odnosi się do przedmiotu, a nie do tagu.


Jeśli próbowałeś a[1] = 'z'dokonać "dog"w "dzg"dostaniemy błąd:

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

ponieważ łańcuchy nie obsługują przypisywania pozycji, dlatego są niezmienne.

chrtan
źródło
19

Coś jest zmienne tylko wtedy, gdy jesteśmy w stanie zmienić wartości przechowywane w lokalizacji pamięci bez zmiany samej lokalizacji pamięci.

Sztuczka polega na tym, że jeśli okaże się, że lokalizacja pamięci przed i po zmianie jest taka sama, jest zmienna.

Na przykład lista jest zmienna. W jaki sposób?

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

Ciąg jest niezmienny. Jak to udowodnimy?

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

dostajemy

TypeError: obiekt „str” nie obsługuje przypisywania pozycji

Więc nie udało nam się zmutować łańcucha. Oznacza to, że ciąg jest niezmienny.

Podczas ponownego przypisywania zmieniasz zmienną, aby wskazywała na nową lokalizację. Tutaj nie zmutowałeś łańcucha, ale zmutowałeś samą zmienną. Oto, co robisz.

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

idprzed i po zmianie jest inny, więc to dowodzi, że tak naprawdę nie mutujesz, ale kierujesz zmienną do nowej lokalizacji. Co nie jest mutacją tego ciągu, ale mutacją tej zmiennej.

Harish Kayarohanam
źródło
11

Zmienna to po prostu etykieta wskazująca na obiekt. Obiekt jest niezmienny, ale jeśli chcesz, możesz ustawić etykietę na zupełnie inny obiekt.

jcollado
źródło
8

Rozważać:

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

Zauważ, że szesnastkowa lokalizacja pamięci nie zmieniła się, gdy dwukrotnie zapisałem tę samą wartość w zmiennej. Zmieniło się, kiedy zapisałem inną wartość. Ciąg jest niezmienny. Nie z powodu gorliwości, ale dlatego, że płacisz karę za wydajność tworzenia nowego obiektu w pamięci. Zmienna ato tylko etykieta wskazująca na ten adres pamięci. Można go zmienić, aby wskazywał cokolwiek.

Bruno Bronosky
źródło
7

Oświadczenie a = a + " " + b + " " + cmożna podzielić na podstawie wskazówek.

a + " "mówi, daj mi apunkty, których nie można zmienić, i dodaj " "do mojego obecnego zestawu roboczego.

pamięć:

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

+ bmówi, daj mi bpunkty, których nie można zmienić, i dodaj to do bieżącego zestawu roboczego.

pamięć:

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

+ " " + cmówi dodaj " "do bieżącego zestawu. Następnie podaj mi, do jakich cpunktów nie można zmienić i dodaj to do bieżącego zestawu roboczego. pamięć:

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

Na koniec a =mówi ustaw mój wskaźnik, aby wskazywał na wynikowy zestaw.

pamięć:

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

"Dog"jest odzyskiwany, ponieważ żadne więcej wskaźników nie łączy się z jego fragmentem pamięci. Nigdy nie modyfikowaliśmy sekcji pamięci, "Dog"w której się znajdowała, co oznacza niezmienność. Możemy jednak zmienić, które etykiety, jeśli w ogóle, wskazują na tę sekcję pamięci.

Spencer Rathbun
źródło
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
user744629
źródło
5

Istnieje różnica między danymi a etykietą, z którą są skojarzone. Na przykład, kiedy to robisz

a = "dog"

dane "dog"są tworzone i umieszczane pod etykietą a. Etykieta może się zmienić, ale to, co jest w pamięci, nie. Po wykonaniu tych czynności dane "dog"nadal będą istnieć w pamięci (do momentu ich usunięcia przez moduł odśmiecania pamięci)

a = "cat"

W twoim programie ateraz ^ wskazuje na ^, "cat"ale łańcuch "dog"się nie zmienił.

mitch
źródło
3

Ciągi Pythona są niezmienne. Jednak anie jest ciągiem znaków: jest to zmienna o wartości ciągu. Nie możesz zmutować ciągu, ale możesz zmienić wartość zmiennej na nowy ciąg.

Michael J. Barber
źródło
2

Zmienne mogą wskazywać dowolne miejsce. Jeśli wykonasz te czynności, zostanie zgłoszony błąd:

a = "dog"
print a                   #dog
a[1] = "g"                #ERROR!!!!!! STRINGS ARE IMMUTABLE
GuruJeya
źródło
2

Obiekty łańcuchowe w Pythonie są niezmienne. Przykład:

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

W tym przykładzie widzimy, że gdy przypisujemy inną wartość w a, to nie modyfikuje, powstaje nowy obiekt
I nie można go modyfikować. Przykład:

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

Wystąpił błąd.

Tanim_113
źródło
2

„mutable” oznacza, że ​​możemy zmienić zawartość ciągu, „immutable” oznacza, że ​​nie możemy dodać dodatkowego ciągu.

kliknij, aby zobaczyć dowód fotograficzny


źródło
1

>>> a = 'dogs'

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

'dogs eat treats'

>>> print a

'dogs'

Niezmienne, prawda ?!

Część dotycząca zmiany zmiennej została już omówiona.

Lohit Bisen
źródło
1
Nie dowodzi to ani nie obala zmienności ciągów znaków w języku Python, tylko że replace()metoda zwraca nowy ciąg.
Brent Hronik
1

Rozważ ten dodatek do swojego przykładu

 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

Jedno z dokładniejszych wyjaśnień, które znalazłem na blogu, to:

W Pythonie (prawie) wszystko jest obiektem. To, co powszechnie nazywamy „zmiennymi” w Pythonie, są bardziej poprawnie nazywane nazwami. Podobnie „przypisanie” jest w rzeczywistości wiązaniem nazwy z obiektem. Każde powiązanie ma zakres, który definiuje jego widoczność, zwykle jest to blok, z którego pochodzi nazwa.

Na przykład:

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

Kiedy później powiemy some_guy = 'George', obiekt string zawierający 'Fred' pozostaje nienaruszony. Właśnie zmieniliśmy powiązanie nazwy some_guy. Nie zmieniliśmy jednak ani obiektów łańcuchowych „Fred”, ani „George”. Jeśli o nas chodzi, mogą żyć w nieskończoność.

Link do bloga: https://jeffknupp.com/blog/2012/11/13/is-python-callbyvalue-or-callbyreference-neither/

7to4
źródło
1

Dodanie nieco więcej do powyższych odpowiedzi.

id zmiennej zmienia się po ponownym przypisaniu.

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

Co oznacza, że ​​zmieniliśmy zmienną, aaby wskazywała na nowy ciąg. Teraz istnieją dwa string (str) obiekty:

'initial_string'gdzie id= 139982120425648

i

'new_string'gdzie id= 139982120425776

Rozważ poniższy kod:

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

Teraz bwskazuje na 'initial_string'i ma to samo, idco aprzed ponownym przypisaniem.

Zatem 'intial_string'nie został zmutowany.

Rahul Madiwale
źródło
0

Zreasumowanie:

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

Niezmienne:

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

Niezmienny:

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

To jest błąd w Pythonie 3, ponieważ jest niezmienny. I nie jest to błąd w Pythonie 2, ponieważ oczywiście nie jest niezmienny.

pjf
źródło
0

Funkcja wbudowana id()zwraca tożsamość obiektu jako liczbę całkowitą. Ta liczba całkowita zwykle odpowiada położeniu obiektu w pamięci.

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

139831803293008

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

139831803293120

Początkowo „a” jest przechowywane w lokalizacji pamięci 139831803293008, ponieważ obiekt ciągu znaków jest niezmienny w Pythonie, jeśli spróbujesz zmodyfikować i ponownie przypisać odniesienie zostanie usunięte i będzie wskaźnikiem do nowej lokalizacji pamięci (139831803293120).

learner5060
źródło
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
10xAI
źródło
2
Chociaż ten kod może rozwiązać problem PO, najlepiej jest dołączyć wyjaśnienie, w jaki sposób Twój kod rozwiązuje problem PO. W ten sposób przyszli odwiedzający mogą uczyć się z Twojego posta i zastosować go do własnego kodu. SO nie jest usługą programistyczną, ale źródłem wiedzy. Ponadto istnieje większe prawdopodobieństwo, że wysokiej jakości, kompletne odpowiedzi zostaną poparte. Te funkcje, wraz z wymogiem, że wszystkie posty są samodzielne, są jednymi z mocnych stron SO jako platformy, która odróżnia ją od forów. Możesz edytować, aby dodać dodatkowe informacje i / lub uzupełnić wyjaśnienia dokumentacją źródłową
SherylHohman
-1

Ten obraz daje odpowiedź. Proszę przeczytaj to.

wprowadź opis obrazu tutaj

Wen Qi
źródło
-1

Po prostu łączymy dwie wartości łańcuchowe. Nigdy nie zmieniamy wartości (a). Przed chwilą (a) reprezentują kolejny blok pamięci, który ma wartość „dogdog”. Ponieważ w zapleczu jedna zmienna nigdy nie reprezentuje dwóch bloków pamięci w tym samym czasie. Wartość (a) przed konkatenacją to „pies”. Ale potem (a) reprezentują "psa", ponieważ teraz (a) w zapleczu rep. blok, który ma wartość „dogdog”. A „pies” to przedstawiciel. by (b) i „pies” nie jest liczone jako wartość śmieci, dopóki (b) nie reprezentuje „psa”.

Nieporozumienie polega na tym, że reprezentujemy bloki pamięci (które zawierają dane lub informacje) w zapleczu o tej samej nazwie zmiennej.

dineshmehta
źródło
-2

Możesz uczynić tablicę numpy niezmienną i użyć pierwszego elementu:

numpyarrayname[0] = "write once"

następnie:

numpyarrayname.setflags(write=False)

lub

numpyarrayname.flags.writeable = False
PauluaP
źródło