Typy niezmienne a zmienne


186

Jestem zdezorientowany, czym jest niezmienny typ. Wiem, że floatobiekt jest uważany za niezmienny, z tego rodzaju przykładem z mojej książki:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

Czy jest to uważane za niezmienne ze względu na strukturę / hierarchię klas ?, co floatoznacza, że znajduje się na szczycie klasy i jest własnym wywołaniem metody. Podobny do tego typu przykładu (chociaż moja książka mówi, że dictmożna go modyfikować):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Podczas gdy coś zmiennego ma metody wewnątrz klasy, z tego rodzaju przykładem:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

Poza tym class(SortedKeyDict_a), jeśli przekażę mu ten typ zestawu:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

bez wywoływania examplemetody zwraca słownik. SortedKeyDictZ __new__flagami jako błąd. Próbowałem przekazać liczby całkowite do RoundFloatklasy __new__i nie oznaczało to żadnych błędów.


Możesz także sprawdzić przypisanie listy za pomocą [:] i python, kiedy należy użyć copy.copy, na które również odpowiedziałem, aby uzyskać więcej informacji na temat zmienności.
agf

Odpowiedzi:


232

Co? Pływaki są niezmienne? Ale nie mogę

x = 5.0
x += 7.0
print x # 12.0

Czy to nie „mut” x?

Cóż, zgadzasz się, że ciągi są niezmienne, prawda? Ale możesz zrobić to samo.

s = 'foo'
s += 'bar'
print s # foobar

Wartość zmiennej zmienia się, ale zmienia się poprzez zmianę tego, do czego odnosi się zmienna. Zmienny typ może zmienić w ten sposób, a także może zmienić „na miejscu”.

Oto różnica.

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

Konkretne przykłady

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]

5
To, co wyjaśnisz, oznacza dla mnie: zmienne zmienne są przekazywane przez odniesienie, niezmienne zmienne są przekazywane przez wartość. Czy to jest poprawne ?
Lorenz Meyer

17
Prawie, ale nie do końca. Technicznie wszystkie zmienne są przekazywane przez odwołanie w Pythonie, ale mają semantykę bardziej podobną do przekazywania przez wartość w C. Kontrprzykładem twojej analogii jest, jeśli tak zrobisz def f(my_list): my_list = [1, 2, 3]. W przypadku przekazywania przez odwołanie w C wartość argumentu może się zmienić przez wywołanie tej funkcji. W Pythonie ta funkcja nic nie robi. def f(my_list): my_list[:] = [1, 2, 3]zrobiłby coś.
morningstar

6
Zmienne typy można zmieniać na miejscu. Niezmienne typy nie mogą się zmieniać w miejscu. W ten sposób python postrzega świat. Niezależnie od tego, w jaki sposób zmienne są przekazywane do funkcji.
ychaouche

13
Kluczową różnicą między semantyką Pythona i semantyką C ++ pass-by-reference jest to, że przypisanie nie jest mutacją w Pythonie, a jest w C ++. (Ale oczywiście komplikuje to fakt, że zwiększone przypisanie, jak a += bczasem jest mutacją. A fakt, że przypisanie do części większego obiektu czasami oznacza mutację tego większego obiektu, po prostu nigdy a[0] = bnie powoduje mutacji części - np. Nie mutuje a[0], ale prawdopodobnie mutuje a… Właśnie dlatego lepiej nie próbować
ujmować

2
Uznałem tę odpowiedź za mylącą, ponieważ nie używa ona id (), która jest niezbędna do zrozumienia, co oznacza niezmienne.
pawel_winzig

185

Musisz zrozumieć, że Python reprezentuje wszystkie swoje dane jako obiekty. Niektóre z tych obiektów, takie jak listy i słowniki, można modyfikować, co oznacza, że ​​możesz zmieniać ich zawartość bez zmiany ich tożsamości. Inne obiekty, takie jak liczby całkowite, zmiennoprzecinkowe, łańcuchy i krotki są obiektami, których nie można zmienić. Łatwy sposób to zrozumieć, jeśli spojrzysz na identyfikator obiektu.

Poniżej widać ciąg niezmienny. Nie możesz zmienić jego zawartości. Podniesie a, TypeErrorjeśli spróbujesz to zmienić. Ponadto, jeśli przypiszemy nową treść, zamiast modyfikowanej zawartości tworzony jest nowy obiekt.

>>> s = "abc"
>>>id(s)
4702124
>>> s[0] 
'a'
>>> s[0] = "o"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>>id(s)
4800100
>>> s += "uvw"
>>>id(s)
4800500

Możesz to zrobić za pomocą listy, która nie zmieni tożsamości obiektów

>>> i = [1,2,3]
>>>id(i)
2146718700
>>> i[0] 
1
>>> i[0] = 7
>>> id(i)
2146718700

Aby dowiedzieć się więcej o modelu danych Pythona, zapoznaj się z odnośnikiem do języka Python:


4
+1 Za link do dokumentacji Pythona. Jednak zajęło mi trochę czasu, zanim zdałem sobie sprawę, że dziś musisz odróżnić Python 2 i 3 - zaktualizowałem odpowiedź, aby to podkreślić.
benjamin

107

Typowy niezmienny typ:

  1. Liczby: int(), float(),complex()
  2. niezmiennych sekwencji: str(), tuple(), frozenset(),bytes()

Typowy typ mutable (prawie wszystko inne):

  1. Zmienne sekwencje: list(),bytearray()
  2. składać czcionki do druku: set()
  3. typ odwzorowania: dict()
  4. klasy, instancje klas
  5. itp.

Jednym ze sposobów szybkiego sprawdzenia, czy typ jest zmienny, czy nie, jest użycie id()wbudowanej funkcji.

Przykłady na liczbach całkowitych

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

używając na liście,

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)

11
Dobrze wyjaśnione. Podobał mi się pomysł sprawdzenia przez id(). +1.
Parag Tyagi,

4
Właściwie użycie id()tutaj jest mylące. Dany obiekt zawsze będzie miał ten sam identyfikator podczas swojego życia, ale różne obiekty, które istnieją w różnych czasach, mogą mieć ten sam identyfikator z powodu wyrzucania elementów bezużytecznych.
sierpnia

37

Po pierwsze, to, czy klasa ma metody, czy jaka jest jej struktura, nie ma nic wspólnego ze zmiennością.

intsi floatniezmienne . Jeśli zrobię

a = 1
a += 5

Wskazuje ona nazwę ana 1gdzieś w pamięci na pierwszej linii. W drugiej linii, wygląda się, że 1dodaje 5, dostaje 6, to punkty ana to 6w pamięci - to nie zmienia1 Do 6w jakikolwiek sposób. Ta sama logika dotyczy następujących przykładów, wykorzystując inne niezmienne typy:

b = 'some string'
b += 'some other string'
c = ('some', 'tuple')
c += ('some', 'other', 'tuple')

W przypadku zmiennych typów mogę robić rzeczy, które faktycznie zmieniają wartość, w której są przechowywane w pamięci . Z:

d = [1, 2, 3]

I stworzyliśmy listę miejsc 1, 2oraz 3w pamięci. Jeśli to zrobię

e = d

Wskazuję tylko ena te samelist d punkty. Mogę wtedy zrobić:

e += [4, 5]

A lista, na którą zarówno ei dwskazuje, zostanie zaktualizowana, aby zawierała również lokalizację 4i 5w pamięci.

Jeśli wrócę do niezmiennego typu i zrobię to z tuple:

f = (1, 2, 3)
g = f
g += (4, 5)

Wtedy fnadal wskazuje tylko oryginałtuple - wskazałeś gna zupełnie nowytuple .

Teraz na przykładzie

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Gdzie mijasz

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

(co jest tuplez tuples) jako val, dostajesz błąd, ponieważ tuples nie ma .clear()sposobu - trzeba by przekazać dict(d)jak valdo niego do pracy, w takim przypadku dostaniesz pusty SortedKeyDictwyniku.


2
To bardzo dobre wytłumaczenie. Podobało mi się to pytanie i wiele interesujących (nowych) perspektyw, aby je wyjaśnić.
Failed Scientist

25

Jeśli przyjeżdżasz do Pythona z innego języka (z wyjątkiem tego, który jest podobny do Pythona, takiego jak Ruby) i nalega na zrozumienie go w odniesieniu do tego innego języka, tutaj ludzie zwykle się mylą:

>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!

W Pythonie przypisanie nie jest mutacją w Pythonie.

W C ++, jeśli piszesz a = 2, dzwonisz a.operator=(2), co zmutuje obiekt przechowywany w a. (A jeśli nie było w nim żadnego obiektu a, to błąd).

W Pythonie a = 2nic nie robi na to, co zostało zapisane a; oznacza to po prostu, że 2teraz jest przechowywany a. (A jeśli nie było w nim żadnego obiektu a, to w porządku.)


Ostatecznie jest to część jeszcze głębszego rozróżnienia.

Zmienna w języku takim jak C ++ to wpisana lokalizacja w pamięci. Jeśli ajest to int, oznacza to, że gdzieś jest 4 bajty, o których kompilator wie, że należy je interpretować jako int. Kiedy to zrobisz a = 2, zmienia to, co jest zapisane w tych 4 bajtach pamięci od 0, 0, 0, 1do 0, 0, 0, 2. Jeśli gdzieś jest inna zmienna int, ma ona swoje 4 bajty.

Zmienna w języku takim jak Python jest nazwą obiektu, który ma własne życie. Istnieje obiekt dla liczby 1i inny obiekt dla liczby 2. I anie ma 4 bajtów pamięci, które są reprezentowane jako int, to tylko nazwa wskazująca na 1obiekt. Nie ma sensu a = 2przekształcanie liczby 1 w liczbę 2 (dałoby to każdemu programistowi w Pythonie o wiele za dużo mocy, aby zmienić podstawowe zasady działania wszechświata); zamiast tego po prostu azapomina o 1obiekcie i 2zamiast tego wskazuje na obiekt.


Jeśli więc przypisanie nie jest mutacją, czym jest mutacja?

  • Wywołanie metody, która została udokumentowana, aby mutować, na przykład a.append(b). (Zauważ, że te metody prawie zawsze powracają None). Niezmienne typy nie mają takich metod, zwykle mają zmienne typy.
  • Przypisywanie do części obiektu, takiej jak a.spam = blub a[0] = b. Niezmienne typy nie pozwalają na przypisanie atrybutów lub elementów, typy zmienne zwykle pozwalają na jedno lub drugie.
  • Czasami przy użyciu rozszerzonego zadania a += b, czasem nie. Zmienne typy zwykle mutują wartość; niezmienne typy nigdy tego nie robią i zamiast tego dają ci kopię (obliczają a + b, a następnie przypisują wynik a).

Ale jeśli przypisanie nie jest mutacją, w jaki sposób przypisanie do części mutacji obiektowej? To tam staje się trudne. a[0] = brobi nie mutacji a[0](ponownie, w przeciwieństwie do C ++), ale robi mutacji a(w przeciwieństwie do C ++, z wyjątkiem pośrednio).

Wszystko to sprawia, że ​​prawdopodobnie lepiej nie próbować układać semantyki języka Python w kategoriach języka, w którym się przyzwyczaiłeś, a zamiast tego uczyć się semantyki języka Python na własnych warunkach.


2
Powiedz a = „cześć”. a [0] = 'f' będzie miało 'wydrukuj' wydrukuj 'fi' (czy mam rację do tej pory?), więc jeśli powiesz, że nie mutuje on [0], a raczej, co to oznacza ? Czy [n] też ma teraz swoje własne miejsce, a zmiana jego wartości wskazuje inną wartość?
Daniel Springer,

19

To, czy obiekt można modyfikować, zależy od jego typu. Nie zależy to od tego, czy ma pewne metody, ani od struktury hierarchii klas.

Typy zdefiniowane przez użytkownika (tj. Klasy) są generalnie zmienne. Istnieją pewne wyjątki, takie jak proste podklasy niezmiennego typu. Inne typy niezmienne obejmują niektóre typy wbudowane, takie jak int, float, tuplei str, a także niektóre klasy Pythona realizowane w C.

Ogólne wyjaśnienie z rozdziału „Model danych” w Skorowidzu języka Python ” :

Wartość niektórych obiektów może ulec zmianie. Obiekty, których wartość może ulec zmianie, są nazywane zmiennymi; obiekty, których wartość jest niezmienna po ich utworzeniu, nazywane są niezmiennymi.

(Wartość niezmiennego obiektu kontenerowego, który zawiera odwołanie do obiektu zmiennego, może ulec zmianie, gdy wartość tego ostatniego zostanie zmieniona; jednak kontener jest nadal uważany za niezmienny, ponieważ nie można zmienić zbioru obiektów w nim zawartych. Zatem niezmienność nie jest ściśle tak samo jak posiadanie niezmiennej wartości, jest bardziej subtelne).

Zmienność obiektu zależy od jego typu; na przykład liczby, łańcuchy i krotki są niezmienne, podczas gdy słowniki i listy są zmienne.


+1 Pamiętaj jednak, że tylko niektóre typy rozszerzeń (możesz chcieć przejrzeć definicję tego, wszystkie wbudowane typy Pythona są zaimplementowane w C) są niezmienne. Inne (większość, śmiałbym powiedzieć) są całkowicie zmienne.

@delnan Jak nazywacie „typy rozszerzeń” ?
eyquem

@eyquem: Użyłem niepoprawnie terminu „typy rozszerzeń” w mojej odpowiedzi i delnan miał na myśli to. Po jego komentarzu poprawiłem odpowiedź i unikałem używania tego terminu.
taleinat

19

Różnica między obiektami zmiennymi i niezmiennymi

Definicje

Zmienny obiekt : Obiekt, który można zmienić po utworzeniu.
Niezmienny obiekt : Obiekt, którego nie można zmienić po utworzeniu.

W Pythonie, jeśli zmienisz wartość niezmiennego obiektu, utworzy on nowy obiekt.

Zmienne obiekty

Oto obiekty w Pythonie, które są typu zmiennego:

  1. list
  2. Dictionary
  3. Set
  4. bytearray
  5. user defined classes

Niezmienne przedmioty

Oto obiekty w Pythonie, które są niezmiennego typu:

  1. int
  2. float
  3. decimal
  4. complex
  5. bool
  6. string
  7. tuple
  8. range
  9. frozenset
  10. bytes

Niektóre pytania bez odpowiedzi

Pytania : Czy ciąg znaków jest niezmienny?
Odpowiedź : tak , ale czy możesz to wyjaśnić: Dowód 1 :

a = "Hello"
a +=" World"
print a

Wynik

"Hello World"

W powyższym przykładzie ciąg został kiedyś utworzony jako „Hello”, a następnie zmieniony na „Hello World”. Oznacza to, że ciąg jest typu zmiennego. Nie dzieje się tak jednak, gdy sprawdzamy jego tożsamość, aby sprawdzić, czy jest typu zmiennego, czy nie.

a = "Hello"
identity_a = id(a)
a += " World"
new_identity_a = id(a)
if identity_a != new_identity_a:
    print "String is Immutable"

Wynik

String is Immutable

Dowód 2 :

a = "Hello World"
a[0] = "M"

Wynik

TypeError 'str' object does not support item assignment

Pytania : Czy Tuple jest niezmiennym typem?
Odpowiedź : tak jest. Dowód 1 :

tuple_a = (1,)
tuple_a[0] = (2,)
print a

Wynik

'tuple' object does not support item assignment

In [46]: a = „Hello” In [47]: id (a) Out [47]: 140071263880128 In [48]: a = a.replace („H”, „g”) In [49]: a Out [49]: „gello” In [50]: id (a) Out [50]: 140071263881040
Argus Malware

czy zechciałbyś udowodnić problem z przypisaniem przedmiotu do mojego powyższego przykładu
Argus Malware

przypisanie przedmiotu nie występuje w niezmiennych typach. W twoim przypadku zmieniasz ciąg a, ale w pamięci przypisujesz go do nowej zmiennej. Przypisanie pozycji w moim przypadku nie zmieni pamięci zmiennej, tak jak w przypadku listy lub słownika. jeśli to robisz zamień, tworzysz nową zmienną, nie modyfikując istniejącej zmiennej
i tripathi

@ArgusMalware w twoim przypadku dwa id są równe, ponieważ pierwszy został ponownie przetworzony przez GC, więc drugi ponownie wykorzystuje pamięć.
Cologler

11

Zmienny obiekt musi mieć przynajmniej metodę zdolną do zmutowania obiektu. Na przykład listobiekt ma appendmetodę, która faktycznie zmutuje obiekt:

>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']

ale klasa floatnie ma metody mutacji obiektu zmiennoprzecinkowego. Możesz to zrobić:

>>> b = 5.0 
>>> b = b + 0.1
>>> b
5.1

ale =operand nie jest metodą. Po prostu tworzy powiązanie między zmienną a tym, co jest po jej prawej stronie, niczym więcej. Nigdy nie zmienia ani nie tworzy obiektów. Jest to deklaracja tego, na co wskazuje zmienna, od teraz.

Kiedy robisz operand wiąże zmienną do nowej float, w którym jest utworzony TE wyniku .b = b + 0.1=5 + 0.1

Po przypisaniu zmiennej do istniejącego obiektu, zmiennego lub nie, =operand wiąże zmienną z tym obiektem. I nic więcej się nie dzieje

W obu przypadkach =wystarczy wykonać powiązanie. Nie zmienia ani nie tworzy obiektów.

Gdy to zrobisz a = 1.0, =operand nie tworzy pływaka, ale 1.0część linii. Właściwie podczas pisania 1.0jest to skrót dla float(1.0)wywołania konstruktora zwracającego obiekt zmiennoprzecinkowy. (Z tego powodu, jeśli wpiszesz 1.0i naciśniesz Enter, wyświetli się „echo” 1.0wydrukowane poniżej; to jest wartość zwracana przez wywołaną funkcję konstruktora)

Teraz, jeśli bjest pływak i przypisać a = bobie zmienne są skierowane do tego samego obiektu, ale w rzeczywistości zmienne sami nie mogą porozumieć betweem, ponieważ obiekt jest inmutable, a jeśli nie b += 1, teraz bpunkt do nowego obiektu, a ajest wciąż wskazuje na oldone i nie może wiedzieć, na co bwskazuje.

ale jeśli cjest, powiedzmy, a list, a ty przypisujesz a = c, teraz ai cmożesz „komunikować się”, ponieważ listjest zmienny, a jeśli tak c.append('msg'), to po prostu sprawdź, aczy dostałeś wiadomość.

(Nawiasem mówiąc, do każdego obiektu przypisany jest unikalny numer identyfikacyjny, który można uzyskać id(x). Możesz więc sprawdzić, czy obiekt jest taki sam lub nie sprawdzać, czy jego unikalny identyfikator się zmienił).


6

Klasa jest niezmienna, jeśli każdy obiekt tej klasy ma ustaloną wartość przy tworzeniu, której nie można PONOWNIE zmienić

Innymi słowy zmień całą wartość tej zmiennej (name)lub zostaw ją w spokoju.

Przykład:

my_string = "Hello world" 
my_string[0] = "h"
print my_string 

spodziewałeś się, że to zadziała i wydrukuje hello world, ale spowoduje to zgłoszenie następującego błędu:

Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment

Tłumacz mówi: nie mogę zmienić pierwszego znaku tego ciągu

będziesz musiał zmienić całość string, aby działała:

my_string = "Hello World" 
my_string = "hello world"
print my_string #hello world

sprawdź tę tabelę:

wprowadź opis zdjęcia tutaj

źródło


Jak można zmodyfikować komponenty łańcucha python w bardziej zwięzły sposób niż pokazano powyżej?
Luke Davis,

@LukeDavis Możesz zrobić my_string = 'h' + my_string[1:]. Spowoduje to wygenerowanie nowego ciągu o nazwie my_string, a oryginalny my_string zniknie (wydrukuj, id(my_string)aby to zobaczyć). Oczywiście nie jest to zbyt elastyczne, w bardziej ogólnym przypadku można przekonwertować na listę iz powrotem:l = list(my_string) l[0] = 'h' my_string = ''.join(l)
danio

5

Wydaje mi się, że walczysz z pytaniem, co tak naprawdę oznacza zmienność / niezmienność . Oto proste wyjaśnienie:

Najpierw potrzebujemy podstaw, na których oprą się badania.

Pomyśl więc o wszystkim, co programujesz jako o obiekcie wirtualnym, o czymś, co jest zapisane w pamięci komputera jako ciąg liczb binarnych. (Nie próbuj jednak zbytnio sobie tego wyobrażać. ^^) Teraz w większości języków komputerowych nie będziesz pracować bezpośrednio z tymi liczbami binarnymi, ale raczej używasz interpretacji liczb binarnych.

Np. Nie myślisz o liczbach takich jak 0x110, 0xaf0278297319 lub podobnych, ale zamiast tego myślisz o liczbach takich jak 6 lub Ciągach takich jak „Witaj, świecie”. Mimo to tezy liczb lub ciągów są interpretacją liczby binarnej w pamięci komputera. To samo dotyczy dowolnej wartości zmiennej.

W skrócie: My nie programować z rzeczywistych wartości, ale z interpretacji rzeczywistych wartości binarnych.

Teraz mamy interpretacje, których nie można zmieniać ze względu na logikę i inne „czyste rzeczy”, podczas gdy istnieją interpretacje, które mogą ulec zmianie. Pomyśl na przykład o symulacji miasta, innymi słowy program, w którym jest wiele wirtualnych obiektów, a niektóre z nich to domy. Czy te wirtualne obiekty (domy) mogą zostać zmienione i czy nadal można je uznać za te same domy? Oczywiście, że mogą. Są więc zmienne: można je zmieniać, nie stając się „całkowicie” innym przedmiotem.

Pomyślmy teraz o liczbach całkowitych: są to także obiekty wirtualne (sekwencje liczb binarnych w pamięci komputera). Jeśli więc zmienimy jedną z nich, na przykład zwiększając wartość sześć po drugiej, czy nadal będzie to szóstka? Oczywiście, że nie. Zatem każda liczba całkowita jest niezmienna.

Tak więc: jeśli jakakolwiek zmiana w obiekcie wirtualnym oznacza, że ​​faktycznie staje się on kolejnym obiektem wirtualnym, to nazywa się to niezmiennym.

Uwagi końcowe:

(1) Nigdy nie mieszaj swojego prawdziwego doświadczenia ze zmiennością i niezmiennością z programowaniem w określonym języku:

Każdy język programowania ma własną definicję, na których obiektach można wyciszać, a które nie.

Chociaż możesz teraz zrozumieć różnicę w znaczeniu, nadal musisz nauczyć się faktycznej implementacji dla każdego języka programowania. ... Rzeczywiście może istnieć cel języka, w którym 6 może zostać wyciszony, aby stać się 7. W takim razie byłyby to całkiem szalone lub interesujące rzeczy, takie jak symulacje równoległych wszechświatów. ^^

(2) To odkrycie z pewnością nie jest naukowe, ma pomóc ci zrozumieć różnicę między zmienną a niezmienną.


5

Celem tej odpowiedzi jest stworzenie jednego miejsca, w którym można znaleźć wszystkie dobre pomysły na temat tego, jak stwierdzić, czy masz do czynienia z mutacją / mutacją (niezmienną / zmienną) i, gdzie to możliwe, co z tym zrobić? Są chwile, kiedy mutacja jest niepożądana, a zachowanie Pythona w tym zakresie może wydawać się sprzeczne z intuicją dla koderów przychodzących z innych języków.

Jak na przydatny post @ mina-gabriel:

Analizując powyższe i łącząc w / post przez @ arrakëën:

Co nie może się zmienić nieoczekiwanie?

  • skalary (typy zmiennych przechowujące jedną wartość) nie zmieniają się nieoczekiwanie
    • przykłady numeryczne: int (), float (), complex ()
  • istnieje kilka „sekwencji modyfikowalnych”:
    • str (), tuple (), frozenset (), bytes ()

Co może?

  • lista jak obiekty (listy, słowniki, zestawy, bytearray ())
  • post tutaj mówi także o klasach i instancjach klas, ale może to zależeć od tego, co klasa dziedziczy i / lub jak jest zbudowana.

przez „nieoczekiwanie” mam na myśli to, że programiści z innych języków mogą nie oczekiwać tego zachowania (z wyjątkiem Ruby i może kilku innych języków „podobnych do Pythona”).

Dodanie do tej dyskusji:

Takie zachowanie jest korzystne, gdy zapobiega przypadkowemu zapełnieniu kodu wielowymiarowymi kopiami dużych struktur danych zajmujących pamięć. Ale kiedy jest to niepożądane, jak sobie z tym poradzić?

W przypadku list prostym rozwiązaniem jest zbudowanie nowego takiego:

list2 = lista (lista1)

z innymi strukturami ... rozwiązanie może być trudniejsze. Jednym ze sposobów jest zapętlenie elementów i dodanie ich do nowej pustej struktury danych (tego samego typu).

funkcje mogą mutować oryginał, gdy przechodzisz w zmienne struktury. Jak powiedzieć?

  • Istnieje kilka testów dotyczących innych komentarzy w tym wątku, ale są też komentarze wskazujące, że testy te nie są w pełni dowodowe
  • object.function () jest metodą oryginalnego obiektu, ale tylko niektóre z nich mutują. Jeśli nic nie zwrócą, prawdopodobnie tak. Można się spodziewać, że .append () mutuje bez testowania, biorąc pod uwagę jego nazwę. .union () zwraca unii set1.union (set2) i nie mutuje. W razie wątpliwości można sprawdzić funkcję pod kątem wartości zwracanej. Jeśli return = None, nie mutuje.
  • sorted () może być rozwiązaniem w niektórych przypadkach. Ponieważ zwraca posortowaną wersję oryginału, może umożliwić przechowywanie niezmutowanej kopii przed rozpoczęciem pracy z oryginałem na inne sposoby. Jednak ta opcja zakłada, że ​​nie obchodzi cię kolejność oryginalnych elementów (jeśli tak, musisz znaleźć inny sposób). Natomiast .sort () mutuje oryginał (jak można się spodziewać).

Podejścia niestandardowe (w razie potrzeby): Znaleziono to na github opublikowanym na licencji MIT:

  • repozytorium github pod: tobgu o nazwie: pyrsistent
  • Co to jest: trwały kod struktury danych w języku Python napisany do użycia zamiast podstawowych struktur danych, gdy mutacja jest niepożądana

W przypadku klas niestandardowych @semicolon sugeruje sprawdzenie, czy istnieje __hash__funkcja, ponieważ zmienne obiekty zasadniczo nie powinny mieć __hash__()funkcji.

To wszystko, co na razie zgromadziłem na ten temat. Inne pomysły, poprawki itp. Są mile widziane. Dzięki.


3

Jeden sposób myślenia o różnicy:

Przypisania niezmiennych obiektów w pythonie można traktować jako głębokie kopie, podczas gdy przypisania do obiektów zmiennych są płytkie


1
To jest niepoprawne. Wszystkie zadania w Pythonie są referencyjne. Kopiowanie nie jest wymagane.
sierpień

3

Najprostsza odpowiedź:

Zmienna zmienna to taka, której wartość może ulec zmianie w miejscu, natomiast w zmiennej niezmiennej zmiana wartości nie nastąpi. Modyfikacja niezmiennej zmiennej odbuduje tę samą zmienną.

Przykład:

>>>x = 5

Utworzy wartość 5, do której odnosi się x

x -> 5

>>>y = x

To stwierdzenie spowoduje, że y odniesie się do 5 z x

x -------------> 5 <----------- y

>>>x = x + y

Ponieważ x jest liczbą całkowitą (typ niezmienny) został odbudowany.

W oświadczeniu wyrażenie na RHS będzie miało wartość 10, a gdy zostanie ono przypisane do LHS (x), x przebuduje się na 10. Więc teraz

x ---------> 10

y ---------> 5


-1

Nie przeczytałem wszystkich odpowiedzi, ale wybrana odpowiedź jest nieprawidłowa i myślę, że autor ma pomysł, że możliwość zmiany przypisania zmiennej oznacza, że ​​każdy typ danych jest zmienny. Tak nie jest. Zmienność ma związek z przekazywaniem przez odniesienie, a nie przekazywaniem wartości.

Powiedzmy, że utworzyłeś listę

a = [1,2]

Gdybyś powiedział:

b = a
b[1] = 3

Nawet jeśli ponownie przypisałeś wartość na B, to również zmieni przypisanie wartości na a. Jest tak, ponieważ kiedy przypisujesz „b = a”. Przekazujesz „odwołanie” do obiektu, a nie kopię wartości. Nie dotyczy to ciągów, liczb zmiennoprzecinkowych itp. To sprawia, że ​​lista, słowniki i podobne zmienne są zmienne, ale wartości logiczne, zmiennoprzecinkowe itp. Są niezmienne.


-1

W przypadku obiektów niezmiennych przypisanie tworzy na przykład nową kopię wartości.

x=7
y=x
print(x,y)
x=10 # so for immutable objects this creates a new copy so that it doesnot 
#effect the value of y
print(x,y)

W przypadku obiektów zmiennych przypisanie nie tworzy kolejnej kopii wartości. Na przykład,

x=[1,2,3,4]
print(x)
y=x #for immutable objects assignment doesn't create new copy 
x[2]=5
print(x,y) # both x&y holds the same list

1
Absolutnie niepoprawne. Przydział nigdy nie tworzy kopii . Przeczytaj nedbatchelder.com/text/names.html W pierwszym przypadku x=10jest to po prostu inne zadanie , podczas gdy x[2] = 5wywołuje metodę mutatora. intobiektom po prostu brakuje metod mutatora , ale semantyka przypisania pytona nie zależy od typu
juanpa.arrivillaga

-2

W Pythonie istnieje prosty sposób na sprawdzenie:

Niezmienny:

    >>> s='asd'
    >>> s is 'asd'
    True
    >>> s=None
    >>> s is None
    True
    >>> s=123
    >>> s is 123
    True

Zmienny:

>>> s={}
>>> s is {}
False
>>> {} is {}
Flase
>>> s=[1,2]
>>> s is [1,2]
False
>>> s=(1,2)
>>> s is (1,2)
False

I:

>>> s=abs
>>> s is abs
True

Myślę więc, że wbudowana funkcja jest również niezmienna w Pythonie.

Ale tak naprawdę nie rozumiem, jak działa float:

>>> s=12.3
>>> s is 12.3
False
>>> 12.3 is 12.3
True
>>> s == 12.3
True
>>> id(12.3)
140241478380112
>>> id(s)
140241478380256
>>> s=12.3
>>> id(s)
140241478380112
>>> id(12.3)
140241478380256
>>> id(12.3)
140241478380256

To takie dziwne.


Ale to oczywiście nie jest poprawne. Ponieważ krotki są niezmienne. Wpisz, x = (1, 2)a następnie spróbuj mutować x, nie jest to możliwe. Jednym ze sposobów, w jaki znalazłem sprawdzanie zmienności, jest to hash, że działa ono przynajmniej dla wbudowanych obiektów. hash(1) hash('a') hash((1, 2)) hash(True)cała praca i hash([]) hash({}) hash({1, 2})wszyscy nie działają.
średnik

@semicolon W przypadku klas zdefiniowanych przez użytkownika hash()będzie działać, jeśli obiekt __hash__()zdefiniuje metodę, nawet jeśli klasy zdefiniowane przez użytkownika są ogólnie zmienne.
sierpnia

1
@ augurar Mam na myśli tak, ale nic w Pythonie nic nie gwarantuje, ponieważ Python nie ma prawdziwego statycznego pisania ani formalnych gwarancji. Ale hashmetoda jest wciąż całkiem dobra, ponieważ zmienne obiekty zasadniczo nie powinny mieć __hash__()metody, ponieważ tworzenie ich kluczy w słowniku jest po prostu niebezpieczne.
średnik

1
@augurar i średnik (lub inne, jeśli go znają): __hash __ () rozwiązanie ... czy twórca klasy niestandardowej musi ją dodać, aby tam była? Jeśli tak, to reguła brzmi: jeśli istnieje, obiekt powinien być niezmienny; jeśli nie istnieje, nie możemy powiedzieć, ponieważ twórca mógł po prostu odejść, jeśli jest wyłączony.
TMWP
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.