Python este un limbaj minunat. Datorită simplității sale, mulți oameni îl aleg ca primul lor limbaj de programare.
Programatorii cu experiență folosesc Python tot timpul, datorită comunității sale largi, abundenței pachetelor și sintaxei clare.
Dar există o problemă care pare să încurce atât începătorii, cât și unii dezvoltatori experimentați: obiectele Python. Mai exact, diferența dintre mutabil și imuabil obiecte.
În această postare ne vom aprofunda cunoștințele despre obiectele Python, vom învăța diferența dintre mutabil și imuabil obiecte și să vedem cum putem folosi interpret pentru a înțelege mai bine cum funcționează Python.
Vom folosi funcții importante și cuvinte cheie precum id
și is
, și vom înțelege diferența dintre x == y
și x is y
.
Ești pregătit pentru? Să începem.
În Python, totul este un obiect
Spre deosebire de alte limbaje de programare în care limbajul suporturi obiecte, în Python într-adevăr Tot este un obiect – inclusiv numere întregi, liste și chiar funcții.
Putem folosi interpretul nostru pentru a verifica dacă:
>>> isinstance(1, object)
True
>>> isinstance(False, object)
True
def my_func():
return "hello"
>>> isinstance(my_func, object)
True
Python are o funcție încorporată, id
, care returnează adresa unui obiect în memorie. De exemplu:
>>> x = 1
>>> id(x)
1470416816
id(obj)
returnează adresa de obj
in memoriaMai sus, am creat un obiect în numele lui x
, și i-a atribuit valoarea 1
. Am folosit apoi id(x)
și am descoperit că acest obiect se găsește la adresa 1470416816
in memoria.
Acest lucru ne permite să verificăm lucruri interesante despre Python. Să presupunem că creăm două variabile în Python – una cu numele de x
și unul cu numele de y
– și atribuiți-le aceeași valoare. De exemplu, aici:
>>> x = "I love Python!"
>>> y = "I love Python!"
Putem folosi operatorul de egalitate (==
) pentru a verifica dacă într-adevăr au aceeași valoare în ochii lui Python:
>>> x == y
True
Dar acestea sunt același obiect din memorie? În teorie, aici pot exista două scenarii foarte diferite.
Conform scenariului (1), avem într-adevăr două obiecte diferite, unul cu numele de x
, și altul cu numele de y
, care se întâmplă să aibă aceeași valoare.
Totuși, ar putea fi și cazul în care Python stochează aici de fapt doar un singur obiect, care are două nume care îl referă – așa cum se arată în scenariu (2):

Putem folosi id
funcție introdusă mai sus pentru a verifica acest lucru:
>>> x = "I love Python!"
>>> y = "I love Python!"
>>> x == y
True
>>> id(x)
52889984
>>> id(y)
52889384
Așa cum putem vedea, comportamentul lui Python se potrivește cu scenariul (1) descris mai sus. Chiar dacă x == y
în acest exemplu (adică x
și y
au aceleași valori), sunt obiecte diferite în memorie. Asta pentru ca id(x) != id(y)
, după cum putem verifica în mod explicit:
>>> id(x) == id(y)
False
Există o modalitate mai scurtă de a face comparația de mai sus, și anume folosind Python’s is
operator. Verificarea dacă x is y
este la fel ca verificarea id(x) == id(y)
, ceea ce înseamnă dacă x
și y
sunt același obiect în memorie:
>>> x == y
True
>>> id(x) == id(y)
False
>>> x is y
False
Acest lucru aruncă o lumină asupra diferenței importante dintre operatorul egalității ==
și operatorul de identitate is
.
După cum puteți vedea în exemplul de mai sus, este complet posibil pentru două nume în Python (x
și y
) să fie legat de două obiecte diferite (și astfel, x is y
este False
), unde aceste două obiecte au aceeași valoare (deci x == y
este True
).
Cum putem crea o altă variabilă care indică același obiect pe care x
arata spre? Putem folosi pur și simplu operatorul de atribuire =
, ca astfel:
>>> x = "I love Python!"
>>> z = x
Pentru a verifica dacă într-adevăr indică același obiect, putem folosi is
operator:
>>> x is z
True
Desigur, acest lucru înseamnă că au aceeași adresă în memorie, așa cum putem verifica în mod explicit folosind id
:
>>> id(x)
54221824
>>> id(z)
54221824
Și, desigur, au aceeași valoare, așa că ne așteptăm x == z
a se intoarce True
de asemenea:
>>> x == z
True
Obiecte mutabile și imuabile în Python
Am spus că totul în Python este un obiect, totuși există o distincție importantă între obiecte. Unele obiecte sunt mutabil în timp ce unii sunt imuabil.
După cum am menționat anterior, acest fapt provoacă confuzie pentru mulți oameni care sunt noi în Python, așa că ne vom asigura că este clar.
Obiecte imuabile în Python
Pentru unele tipuri din Python, odată ce am creat instanțe ale acestor tipuri, acestea nu se schimbă niciodată. Sunt imuabil.
De exemplu, int
obiectele sunt imuabile în Python. Ce se va întâmpla dacă încercăm să schimbăm valoarea unui int
obiect?
>>> x = 24601
>>> x
24601
>>> x = 24602
>>> x
24602
Ei bine, se pare că ne-am schimbat x
cu succes. Exact aici se confundă mulți oameni. Ce s-a întâmplat exact sub capotă aici? Să folosim id
pentru a investiga în continuare:
>>> x = 24601
>>> x
24601
>>> id(x)
1470416816
>>> x = 24602
>>> x
24602
>>> id(x)
1470416832
Deci, putem vedea asta prin atribuire x = 24602
, nu am schimbat valoarea obiectului care x
fusese legat până acum. Mai degrabă, am creat un obiect nou și am legat numele x
la ea.
Deci, după atribuire 24601
la x
prin utilizarea x = 24601
, am avut următoarea stare:

Și după utilizare x = 24602
, am creat un nou obiect și am legat numele x
la acest nou obiect. Celălalt obiect cu valoarea lui 24601
nu mai este accesibil prin x
(sau orice alt nume în acest caz):

Ori de câte ori atribuim o nouă valoare unui nume (în exemplul de mai sus – x
) care este legat de un int
obiect, schimbăm de fapt legarea acelui nume la un alt obiect.
Același lucru este valabil și pentru tuple
s, corzi (str
obiecte) și bool
s, de asemenea. Cu alte cuvinte, int
(și alte tipuri de numere, cum ar fi float
), tuple
, bool
, și str
obiectele sunt imuabil.
Să testăm această ipoteză. Ce se întâmplă dacă creăm un tuple
obiect și apoi să-i dai o valoare diferită?
>>> my_tuple = (1, 2, 3)
>>> id(my_tuple)
54263304
>>> my_tuple = (3, 4, 5)
>>> id(my_tuple)
56898184
La fel ca un int
obiect, putem vedea că misiunea noastră a schimbat de fapt obiectul pe care îl numea my_tuple
este legat de.
Ce se întâmplă dacă încercăm să schimbăm unul dintre tuple
elementele?
>>> my_tuple[0] = 'a new value'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
După cum putem vedea, Python nu ne permite să modificăm my_tuple
conținutul său, deoarece este imuabil.
Obiecte mutabile în Python
Unele tipuri din Python pot fi modificate după creare și sunt numite mutabil. De exemplu, știm că putem modifica conținutul unui list
obiect:
>>> my_list = [1, 2, 3]
>>> my_list[0] = 'a new value'
>>> my_list
['a new value', 2, 3]
Asta înseamnă că am creat de fapt un nou obiect atunci când atribuim o nouă valoare primului element al my_list
? Din nou, putem folosi id
a verifica:
>>> 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]
Deci prima noastră sarcină my_list = [1, 2, 3]
a creat un obiect în adresă 55834760
, cu valorile lui 1
, 2
, și 3
:

Am modificat apoi primul element al acestui lucru list
obiect folosind my_list[0] = 'a new value'
, adică – fără a crea un nou list
obiect:

Acum, haideți să creăm două nume – x
și y
, ambele legate de același lucru list
obiect. Putem verifica acest lucru fie folosind is
, sau prin verificarea explicită a acestora id
s:
>>> x = y = [1, 2]
>>> x is y
True
>>> id(x)
18349096
>>> id(y)
18349096
>>> id(x) == id(y)
True
Ce se întâmplă acum dacă folosim x.append(3)
? Adică, dacă adăugăm un element nou (3
) către obiectul cu numele de x
?
Voi x
prin schimbat? Voi y
?
Ei bine, după cum știm deja, acestea sunt în principiu două nume ale aceluiași obiect:

Deoarece acest obiect este schimbat, când îi verificăm numele, putem vedea noua valoare:
>>> x.append(3)
>>> x
[1, 2, 3]
>>> y
[1, 2, 3]
Rețineți că x
și y
au aceleași id
ca și înainte – întrucât sunt încă legați de același lucru list
obiect:
>>> id(x)
18349096
>>> id(y)
18349096

Pe lângă list
s, alte tipuri Python care pot fi modificate includ set
s și dict
s.
Implicații pentru cheile de dicționar în Python
Dicționare (dict
obiecte) sunt utilizate în mod obișnuit în Python. Ca un memento rapid, le definim astfel:
my_dict = {"name": "Omer", "number_of_pets": 1}
Putem apoi accesa un anumit element prin numele cheii sale:
>>> my_dict["name"]
'Omer'
Dicționarele sunt mutabil, astfel încât să le putem schimba conținutul după creare. În orice moment, o cheie din dicționar poate indica un singur element:
>>> my_dict["name"] = "John"
>>> my_dict["name"]
'John'
Este interesant de observat că a cheile dicționarului trebuie să fie imuabile:
>>> my_dict = {[1,2]: "Hello"}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
De ce este așa?
Să luăm în considerare următorul scenariu ipotetic (notă: fragmentul de mai jos nu poate fi rulat într-adevăr în Python):
>>> x = [1, 2]
>>> y = [1, 2, 3]
>>> my_dict = {x: 'a', y: 'b'}
Până acum, lucrurile nu par atât de rele. Am presupune asta dacă accesăm my_dict
cu cheia de [1, 2]
, vom obține valoarea corespunzătoare a 'a'
, și dacă accesăm cheia [1, 2, 3]
, vom obține valoarea 'b'
.
Acum, ce s-ar întâmpla dacă am încerca să folosim:
>>> x.append(3)
În acest caz, x
ar avea valoarea de [1, 2, 3]
, și y
ar avea și valoarea de [1, 2, 3]
. Ce ar trebui să obținem atunci când cerem my_dict[[1, 2, 3]]
? Va fi 'a'
sau 'b'
? Pentru a evita astfel de cazuri, Python pur și simplu nu permite modificarea cheilor din dicționar.
Luând lucrurile puțin mai departe
Să încercăm să ne aplicăm cunoștințele într-un caz puțin mai interesant.
Mai jos, definim un list
(A mutabil obiect) și a tuple
(un imuabil obiect). list
include un tuple
, si tuple
include un list
:
>>> my_list = [(1, 1), 2, 3]
>>> my_tuple = ([1, 1], 2, 3)
>>> type(my_list)
<class 'list'>
>>> type(my_list[0])
<class 'tuple'>
>>> type(my_tuple)
<class 'tuple'>
>>> type(my_tuple[0])
<class 'list'>
Până acum, bine. Acum, încearcă să te gândești singur – ce se va întâmpla când încercăm să executăm fiecare dintre următoarele afirmații?
(1) >>> my_list[0][0] = 'Changed!'
(2) >>> my_tuple[0][0] = 'Changed!'
În declarația (1), ceea ce încercăm să facem este să ne schimbăm my_list
primul element, adică a tuple
. Din moment ce a tuple
este imuabil, această încercare este destinată eșecului:
>>> my_list[0][0] = 'Changed!'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Rețineți că ceea ce încercam să facem este nu schimbați lista, ci mai degrabă – schimbați conținutul primului său element.
Să luăm în considerare afirmația (2). În acest caz, accesăm my_tuple
primul element, care se întâmplă să fie un list
, și modificați-l. Să investigăm în continuare acest caz și să analizăm adresele acestor elemente:
>>> my_tuple = ([1, 1], 2, 3)
>>> id(my_tuple)
20551816
>>> type(my_tuple[0])
<class 'list'>
>>> id(my_tuple[0])
20446248

Când ne schimbăm my_tuple[0][0]
, nu ne schimbăm cu adevărat my_tuple
deloc! Într-adevăr, după schimbare, my_tuple
Primul element va fi în continuare obiectul a cărui adresă este în memorie 20446248
. Cu toate acestea, schimbăm valoarea acelui obiect:
>>> my_tuple[0][0] = 'Changed!'
>>> id(my_tuple)
20551816
>>> id(my_tuple[0])
20446248
>>> my_tuple
(['Changed!', 1], 2, 3)
id(my_tuple)
și id(my_tuple[0])
rămâne la fel după schimbare
Deoarece am modificat doar valoarea lui my_tuple[0]
, care este un mutabil list
obiect, această operațiune a fost într-adevăr permisă de Python.
Recapitulare
În această postare am aflat despre obiectele Python. Am spus asta în Python totul este un obiect, și a ajuns să folosesc id
și is
pentru a ne aprofunda înțelegerea a ceea ce se întâmplă sub capotă atunci când folosim Python pentru a crea și modifica obiecte.
De asemenea, am învățat diferența dintre mutabil obiecte, care pot fi modificate după creare și imuabil obiecte, care nu pot.
Am văzut că atunci când îi cerem lui Python să modifice un obiect imuabil care este legat de un anumit nume, de fapt creăm un obiect nou și îl legăm de acesta.
Am aflat apoi de ce trebuie să fie cheile de dicționar imuabil în Python.
Înțelegerea modului în care Python „vede” obiectele este o cheie pentru a deveni un programator Python mai bun. Sper că această postare te-a ajutat în călătoria ta către stăpânirea Python.
Omer Rosenbaum, ÎnotDirector tehnologic. Expert în formare cibernetică și fondator al Checkpoint Security Academy. Autor al Rețele de calculatoare (în ebraică). Vizitează-l pe My Canalul canalului YouTube.