Mutable vs Immutable Objects in Python - En visuel og praktisk vejledning

Python er et fantastisk sprog. På grund af sin enkelhed vælger mange mennesker det som deres første programmeringssprog.

Erfarne programmører bruger også Python hele tiden takket være dets brede samfund, overflod af pakker og klar syntaks.

Men der er et problem, der synes at forvirre begyndere såvel som nogle erfarne udviklere: Python-objekter. Specifikt forskellen mellem foranderlige og uforanderlige objekter.

I dette indlæg vil vi uddybe vores viden om Python-objekter, lære forskellen mellem foranderlige og uforanderlige objekter og se, hvordan vi kan bruge tolken til bedre at forstå, hvordan Python fungerer.

Vi bruger vigtige funktioner og nøgleord som idog is, og vi forstår forskellen mellem x == yog x is y.

Er du klar til det? Lad os komme igang.

I Python er alt et objekt

I modsætning til andre programmeringssprog, hvor de sproglige understøtter objekter, i Python virkelig alt er et objekt - herunder heltal, lister og endda funktioner.

Vi kan bruge vores tolk til at kontrollere, at:

>>> isinstance(1, object) True >>> isinstance(False, object) True def my_func(): return "hello" >>> isinstance(my_func, object) True

Python har en indbygget funktion, idsom returnerer adressen på et objekt i hukommelsen. For eksempel:

>>> x = 1 >>> id(x) 1470416816

Ovenfor oprettede vi et objekt med navnet xog tildelte det værdien af 1. Vi brugte derefter id(x)og opdagede, at dette objekt findes på adressen 1470416816i hukommelsen.

Dette giver os mulighed for at kontrollere interessante ting om Python. Lad os sige, at vi opretter to variabler i Python - en med navnet på xog en med navnet y- og tildeler dem den samme værdi. For eksempel her:

>>> x = "I love Python!" >>> y = "I love Python!"

Vi kan bruge lighedsoperatoren ( ==) til at kontrollere, at de faktisk har den samme værdi i Pythons øjne:

>>> x == y True

Men er disse det samme objekt i hukommelsen? I teorien kan der være to meget forskellige scenarier her.

Ifølge scenario (1) har vi virkelig to forskellige objekter, et ved navn xog et andet med navnet y, der bare tilfældigvis har den samme værdi.

Alligevel kan det også være tilfældet, at Python faktisk kun gemmer et objekt her, som har to navne, der refererer til det - som vist i scenarie (2) :

Vi kan bruge ovenstående idfunktion til at kontrollere dette:

>>> x = "I love Python!" >>> y = "I love Python!" >>> x == y True >>> id(x) 52889984 >>> id(y) 52889384

Så som vi kan se, matcher Pythons adfærd scenarie (1) beskrevet ovenfor. Selvom x == yi dette eksempel (dvs. xog yhar de samme værdier ), er de forskellige objekter i hukommelsen. Dette skyldes id(x) != id(y), som vi kan bekræfte eksplicit:

>>> id(x) == id(y) False

Der er en kortere måde at sammenligne ovenfor på, og det er at bruge Pythons isoperatør. Kontrol af, om det x is yer det samme som kontrol id(x) == id(y), hvilket betyder, om xog yer det samme objekt i hukommelsen:

>>> x == y True >>> id(x) == id(y) False >>> x is y False

Dette kaster lys over den vigtige forskel mellem ligestillingsoperatøren ==og identitetsoperatøren is.

Som du kan se i eksemplet ovenfor, er det fuldstændigt muligt for to navne i Python ( xog y) at være bundet til to forskellige objekter (og dermed x is yer False), hvor disse to objekter har den samme værdi (det x == yer det også True).

Hvordan kan vi oprette en anden variabel, der peger på det samme objekt, xder peger på? Vi kan simpelthen bruge tildelingsoperatøren =som sådan:

>>> x = "I love Python!" >>> z = x

For at kontrollere, at de faktisk peger på det samme objekt, kan vi bruge isoperatøren:

>>> x is z True

Selvfølgelig betyder det, at de har den samme adresse i hukommelsen, som vi eksplicit kan bekræfte ved hjælp af id:

>>> id(x) 54221824 >>> id(z) 54221824

Og selvfølgelig har de samme værdi, så vi forventer også x == zat vende tilbage True:

>>> x == z True

Omskiftelige og uforanderlige objekter i Python

Vi har sagt, at alt i Python er et objekt, alligevel er der en vigtig skelnen mellem objekter. Nogle genstande kan ændres, mens andre er uforanderlige .

Som jeg nævnte før, forårsager denne kendsgerning forvirring for mange mennesker, der er nye for Python, så vi skal sikre os, at det er klart.

Uforanderlige objekter i Python

For nogle typer i Python ændres de, når vi først har oprettet forekomster af disse typer. De er uforanderlige .

For eksempel er intobjekter uforanderlige i Python. Hvad sker der, hvis vi prøver at ændre værdien af ​​et intobjekt?

>>> x = 24601 >>> x 24601 >>> x = 24602 >>> x 24602

Nå ser det ud til, at vi ændrede med xsucces. Dette er præcis, hvor mange mennesker bliver forvirrede. Hvad skete der nøjagtigt under emhætten her? Lad os bruge idtil at undersøge yderligere:

>>> x = 24601 >>> x 24601 >>> id(x) 1470416816 >>> x = 24602 >>> x 24602 >>> id(x) 1470416832

Så vi kan se, at ved at tildele x = 24602ændrede vi ikke værdien af ​​det objekt, der xvar bundet til før. Snarere oprettede vi et nyt objekt og bandt navnet xtil det.

Så efter at tildele 24601til xved hjælp af x = 24601, havde vi følgende tilstand:

Og efter brug x = 24602oprettede vi et nyt objekt og bundet navnet xtil dette nye objekt. Det andet objekt med værdien 24601kan ikke længere nås med x(eller ethvert andet navn i dette tilfælde):

Whenever we assign a new value to a name (in the above example - x) that is bound to an int object, we actually change the binding of that name to another object.

The same applies for tuples, strings (str objects), and bools as well. In other words, int (and other number types such as float), tuple, bool, and str objects are immutable.

Let's test this hypothesis. What happens if we create a tuple object, and then give it a different value?

>>> my_tuple = (1, 2, 3) >>> id(my_tuple) 54263304 >>> my_tuple = (3, 4, 5) >>> id(my_tuple) 56898184

Just like an int object, we can see that our assignment actually changed the object that the name my_tuple is bound to.

What happens if we try to change one of the tuple's elements?

>>> my_tuple[0] = 'a new value' Traceback (most recent call last): File "", line 1, in  TypeError: 'tuple' object does not support item assignment

As we can see, Python doesn't allow us to modify my_tuple's contents, as it is immutable.

Mutable objects in Python

Some types in Python can be modified after creation, and they are called mutable. For example, we know that we can modify the contents of a list object:

>>> my_list = [1, 2, 3] >>> my_list[0] = 'a new value' >>> my_list ['a new value', 2, 3]

Does that mean we actually created a new object when assigning a new value to the first element of my_list? Again, we can use id to check:

>>> my_list = [1, 2, 3] >>> id(my_list) 55834760 >>> my_list [1, 2, 3] >>> my_list[0] = 'a new value' >>> id(my_list) 55834760 >>> my_list ['a new value', 2, 3]

So our first assignment my_list = [1, 2, 3] created an object in the address 55834760, with the values of 1, 2, and 3:

We then modified the first element of this list object using my_list[0] = 'a new value', that is - without creating a new list object:

Now, let us create two names – x and y, both bound to the same list object. We can verify that either by using is, or by explicitly checking their ids:

>>> x = y = [1, 2] >>> x is y True >>> id(x) 18349096 >>> id(y) 18349096 >>> id(x) == id(y) True

What happens now if we use x.append(3)? That is, if we add a new element (3) to the object by the name of x?

Will x by changed? Will y?

Well, as we already know, they are basically two names of the same object:

Since this object is changed, when we check its names we can see the new value:

>>> x.append(3) >>> x [1, 2, 3] >>> y [1, 2, 3]

Note that x and y have the same id as before – as they are still bound to the same list object:

>>> id(x) 18349096 >>> id(y) 18349096

In addition to lists, other Python types that are mutable include sets and dicts.

Implications for dictionary keys in Python

Dictionaries (dict objects) are commonly used in Python. As a quick reminder, we define them like so:

my_dict = {"name": "Omer", "number_of_pets": 1}

We can then access a specific element by its key name:

>>> my_dict["name"] 'Omer'

Dictionaries are mutable, so we can change their content after creation. At any given moment, a key in the dictionary can point to one element only:

>>> my_dict["name"] = "John" >>> my_dict["name"] 'John'

It is interesting to note that a dictionary's keys must be immutable:

>>> my_dict = {[1,2]: "Hello"} Traceback (most recent call last): File "", line 1, in  TypeError: unhashable type: 'list'

Why is that so?

Let's consider the following hypothetical scenario (note: the snippet below can't really be run in Python):

>>> x = [1, 2] >>> y = [1, 2, 3] >>> my_dict = {x: 'a', y: 'b'}

So far, things don't seem that bad. We'd assume that if we access my_dict with the key of [1, 2], we will get the corresponding value of 'a', and if we access the key [1, 2, 3], we will get the value 'b'.

Now, what would happen if we attempted to use:

>>> x.append(3)

In this case, x would have the value of [1, 2, 3], and y would also have the value of [1, 2, 3]. What should we get when we ask for my_dict[[1, 2, 3]]? Will it be 'a' or 'b'? To avoid such cases, Python simply doesn't allow dictionary keys to be mutable.

Taking things a bit further

Let's try to apply our knowledge to a case that is a bit more interesting.

Below, we define a list (a mutable object) and a tuple (an immutable object). The list includes a tuple, and the tuple includes a list:

>>> my_list = [(1, 1), 2, 3] >>> my_tuple = ([1, 1], 2, 3) >>> type(my_list)  >>> type(my_list[0])  >>> type(my_tuple)  >>> type(my_tuple[0]) 

So far so good. Now, try to think for yourself – what will happen when we try to execute each of the following statements?

(1) >>> my_list[0][0] = 'Changed!'

(2) >>> my_tuple[0][0] = 'Changed!'

In statement (1), what we are trying to do is change my_list's first element, that is, a tuple. Since a tuple is immutable, this attempt is destined to fail:

>>> my_list[0][0] = 'Changed!' Traceback (most recent call last): File "", line 1, in  TypeError: 'tuple' object does not support item assignment

Note that what we were trying to do is not change the list, but rather – change the contents of its first element.

Let's consider statement (2). In this case, we are accessing my_tuple's first element, which happens to be a list, and modify it. Let's further investigate this case and look at the addresses of these elements:

>>> my_tuple = ([1, 1], 2, 3) >>> id(my_tuple) 20551816 >>> type(my_tuple[0])  >>> id(my_tuple[0]) 20446248

When we change my_tuple[0][0], we do not really change my_tuple at all! Indeed, after the change, my_tuple's first element will still be the object whose address in memory is 20446248. We do, however, change the value of that object:

>>> my_tuple[0][0] = 'Changed!' >>> id(my_tuple) 20551816 >>> id(my_tuple[0]) 20446248 >>> my_tuple (['Changed!', 1], 2, 3)

Since we only modified the value of my_tuple[0], which is a mutable list object, this operation was indeed allowed by Python.

Recap

In this post we learned about Python objects. We said that in Python everything is an object, and got to use id and is to deepen our understanding of what's happening under the hood when using Python to create and modify objects.

We also learned the difference between mutable objects, that can be modified after creation, and immutable objects, which cannot.

We saw that when we ask Python to modify an immutable object that is bound to a certain name, we actually create a new object and bind that name to it.

Vi lærte derefter, hvorfor ordbogstaster skal være uforanderlige i Python.

At forstå, hvordan Python "ser" objekter, er en nøgle til at blive en bedre Python-programmør. Jeg håber, at dette indlæg har hjulpet dig på din rejse til at mestre Python.

Omer Rosenbaum , Swimms Chief Technology Officer. Ekspert på cyberuddannelse og grundlægger af Checkpoint Security Academy. Forfatter af computernetværk (på hebraisk) . Besøg Min YouTube-kanal .