🔹 Faceți cunoștință cu proprietăți

Bine ati venit! În acest articol, veți învăța cum să lucrați cu @property decorator în Python.

O sa inveti:

  • Avantajele de a lucra cu proprietăți în Python.
  • Noțiunile de bază ale funcțiilor decoratorului: ce sunt acestea și cum sunt legate de @property.
  • Cum puteți utiliza @property pentru a defini getters, setere și ștergeri.

1️⃣ Avantajele proprietăților în Python

Să începem cu un pic de context. De ce ai folosi proprietăți în Python?

Proprietățile pot fi considerate modul “Pythonic” de a lucra cu atribute deoarece:

  • Sintaxa utilizată pentru definirea proprietăților este foarte concisă și lizibilă.
  • Puteți accesa atributele de instanță exact ca și când ar fi atribute publice în timp ce utilizați „magia” intermediarilor (getters și setatori) pentru a valida noi valori și pentru a evita accesarea sau modificarea directă a datelor.
  • Utilizând @property, puteți „refolosi” numele unei proprietăți pentru a evita crearea de nume noi pentru getters, setere și ștergătoare.

Aceste avantaje fac din proprietăți un instrument cu adevărat minunat pentru a vă ajuta să scrieți cod mai concis și mai ușor de citit. ?

ad-banner

2️⃣ Introducere în Decoratori

A funcția de decorator este practic o funcție care adaugă funcționalitate nouă unei funcții care este transmisă ca argument. Utilizarea unei funcții de decor este ca și cum ai adăuga stropi de ciocolată la o înghețată? Ne permite să adăugăm noi funcționalități la o funcție existentă fără a o modifica.

În exemplul de mai jos, puteți vedea cum arată o funcție tipică de decorator în Python:

def decorator(f):
    def new_function():
        print("Extra Functionality")
        f()
    return new_function

@decorator
def initial_function():
    print("Initial Functionality")

initial_function()

Să analizăm în detaliu aceste elemente:

  • Mai întâi găsim funcția de decorator def decorator(f) (stropeste ✨) care ia o funcție f ca argument.
def decorator(f):
    def new_function():
        print("Extra Functionality")
        f()
    return new_function
  • Această funcție decorator are o funcție imbricată, new_function . Observați cum f se numește în interior new_function pentru a obține aceeași funcționalitate în timp ce adăugăm noi funcționalități înainte de apelul de funcție (am putea adăuga și noi funcționalități după apelul de funcție).
  • Funcția decorator în sine returnează funcția imbricată new_function.
  • Apoi (mai jos), găsim funcția care va fi decorat (inghetata ?) initial_function. Observați sintaxa foarte particulară (@decorator) deasupra antetului funcției.
@decorator
def initial_function():
    print("Initial Functionality")

initial_function()

Dacă rulăm codul, vedem această ieșire:

Extra Functionality
Initial Functionality

Observați cum funcționează funcția decorator chiar dacă sunăm doar initial_function(). Aceasta este magia adăugării @decorator?

💡Notă: În general, am scrie @<decorator_function_name>, înlocuind numele funcției decorator după simbolul @.

Știu că s-ar putea să întrebați: ce legătură are acest lucru cu @property? @Property este un decorator încorporat pentru proprietate() funcție în Python. Se folosește pentru a oferi funcționalități „speciale” anumitor metode pentru a le face să acționeze ca generatori, setatori sau ștergători atunci când definim proprietăți într-o clasă.

Acum, că sunteți familiarizați cu decoratorii, să vedem un scenariu real al utilizării @property!

🔸 Scenariu din lumea reală: @property

Să presupunem că această clasă face parte din programul dvs. Modelezi o casă cu un House clasă (în prezent, clasa are doar un Preț atributul instanței definit):

class House:

	def __init__(self, price):
		self.price = price

Acest atribut de instanță este public, deoarece numele său nu are o subliniere principală. Deoarece atributul este public în prezent, este foarte probabil ca dvs. și alți dezvoltatori din echipa dvs. să accesați și să modificați atributul direct în alte părți ale programului folosind notația punct, ca aceasta:

# Access value
obj.price

# Modify value
obj.price = 40000

💡 Bacsis: obiect reprezintă o variabilă care face referire la o instanță de House.

Până acum totul funcționează excelent, nu? Dar să spunem că vi se cere să protejați acest atribut (nepublic) și să validați noua valoare înainte de a-l atribui. Mai exact, trebuie să verificați dacă valoarea este un float pozitiv. Cum ai face asta? Sa vedem.

Schimbarea codului

În acest moment, dacă decideți să adăugați getters și seteri, probabil că dvs. și echipa dvs. veți intra în panică? Acest lucru se datorează faptului că fiecare linie de cod care accesează sau modifică valoarea atributului va trebui modificată pentru a apela respectivul getter sau setter. În caz contrar, codul se va sparge ⚠️.

# Changed from obj.price
obj.get_price()

# Changed from obj.price = 40000
obj.set_price(40000)

Dar … Proprietățile vin în ajutor! Cu @property, dvs. și echipa dvs. nu va trebui să modificați niciuna dintre acele linii, deoarece veți putea adăuga getters și setere „în spatele scenei” fără a afecta sintaxa pe care ați folosit-o pentru a accesa sau modifica atributul când era public.

Minunat, nu?

Prop @property: Sintaxă și logică

Dacă decideți să utilizați @property, clasa dvs. va arăta ca exemplul de mai jos:

class House:

	def __init__(self, price):
		self._price = price

	@property
	def price(self):
		return self._price
	
	@price.setter
	def price(self, new_price):
		if new_price > 0 and isinstance(new_price, float):
			self._price = new_price
		else:
			print("Please enter a valid price")

	@price.deleter
	def price(self):
		del self._price

Mai exact, puteți defini trei metode pentru o proprietate:

  • A getter – pentru a accesa valoarea atributului.
  • A setter – pentru a seta valoarea atributului.
  • A deleter – pentru a șterge atributul instanță.

Prețul este acum „Protejat”
Vă rugăm să rețineți că Preț atributul este acum considerat „protejat” deoarece am adăugat un subliniat principal la numele său în self._price:

self._price = price

În Python, prin convenție, atunci când adăugați un subliniat principal la un nume, îi spuneți altor dezvoltatori că nu ar trebui să fie accesat sau modificat direct în afara clasei. Ar trebui să fie accesat numai prin intermediari (getters și setatori), dacă acestea sunt disponibile.

🔸 Getter

Aici avem metoda getter:

@property
def price(self):
	return self._price

Observați sintaxa:

  • @property – Folosit pentru a indica faptul că vom defini o proprietate. Observați cum acest lucru îmbunătățește imediat lizibilitatea, deoarece putem vedea clar scopul acestei metode.
  • def price(self) – Antetul. Observați cum getter-ul este numit exact ca proprietatea pe care o definim: Preț. Acesta este numele pe care îl vom folosi pentru a accesa și modifica atributul în afara clasei. Metoda ia un singur parametru formal, auto, care este o referință la instanță.
  • return self._price – Această linie este exact ceea ce v-ați aștepta la un getter obișnuit. Se returnează valoarea atributului protejat.

Iată un exemplu de utilizare a metodei getter:

>>> house = House(50000.0) # Create instance
>>> house.price            # Access value
50000.0

Observați cum accesăm Preț atribut ca și cum ar fi un atribut public. Nu schimbăm deloc sintaxa, dar folosim de fapt getterul ca intermediar pentru a evita accesarea directă a datelor.

🔹 Setator

Acum avem metoda setter:

@price.setter
def price(self, new_price):
	if new_price > 0 and isinstance(new_price, float):
		self._price = new_price
	else:
		print("Please enter a valid price")

Observați sintaxa:

  • @price.setter – Folosit pentru a indica faptul că acesta este setter metoda pentru Preț proprietate. Observați că suntem nu folosind @proprietate.setter, folosim @Preț.setter. Numele proprietății este inclus anterior .setter.
  • def price(self, new_price): – Antetul și lista parametrilor. Observați cum este utilizat numele proprietății ca nume al setatorului. Avem, de asemenea, un al doilea parametru formal (pret nou), care este noua valoare care va fi atribuită fișierului Preț atribut (dacă este valid).
  • În sfârșit, avem corpul setterului unde ne aflăm valida argumentul pentru a verifica dacă este un float pozitiv și apoi, dacă argumentul este valid, actualizăm valoarea atributului. Dacă valoarea nu este validă, se tipărește un mesaj descriptiv. Puteți alege cum să gestionați valorile nevalide în funcție de nevoile programului dvs.

Acesta este un exemplu de utilizare a metodei setter cu @property:

>>> house = House(50000.0)  # Create instance
>>> house.price = 45000.0   # Update value
>>> house.price             # Access value
45000.0

Observați cum nu schimbăm sintaxa, dar acum folosim un intermediar (setterul) pentru a valida argumentul înainte de a-l atribui. Noua valoare (45000.0) este transmisă ca argument către setter:

house.price = 45000.0

Dacă încercăm să atribuim o valoare nevalidă, vom vedea mesajul descriptiv. De asemenea, putem verifica dacă valoarea nu a fost actualizată:

>>> house = House(50000.0)
>>> house.price = -50
Please enter a valid price
>>> house.price
50000.0

💡 Bacsis: Acest lucru demonstrează că metoda setter funcționează ca intermediar. Se numește „în culise” atunci când încercăm să actualizăm valoarea, astfel încât mesajul descriptiv este afișat atunci când valoarea nu este validă.

🔸 Deleter

În cele din urmă, avem metoda de eliminare:

@price.deleter
def price(self):
	del self._price

Observați sintaxa:

  • @price.deleter – Folosit pentru a indica faptul că acesta este deleter metoda pentru Preț proprietate. Observați că această linie este foarte asemănătoare cu @ price.setter, dar acum definim metoda deleter, așa că scriem @price.deleter.
  • def price(self): – Antetul. Această metodă are doar un parametru formal definit, auto.
  • del self._price – Corpul, unde ștergem atributul instanță.

💡 Bacsis: Observați că numele proprietății este „reutilizat” pentru toate cele trei metode.

Acesta este un exemplu de utilizare a metodei deleter cu @property:

# Create instance
>>> house = House(50000.0)

# The instance attribute exists
>>> house.price
50000.0

# Delete the instance attribute
>>> del house.price

# The instance attribute doesn't exist
>>> house.price
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    house.price
  File "<pyshell#20>", line 8, in price
    return self._price
AttributeError: 'House' object has no attribute '_price'

Atributul instanță a fost șters cu succes? Când încercăm să-l accesăm din nou, se produce o eroare deoarece atributul nu mai există.

🔹 Câteva sfaturi finale

Nu trebuie neapărat să definiți toate cele trei metode pentru fiecare proprietate. Puteți defini proprietăți numai în citire, incluzând doar o metodă getter. De asemenea, puteți alege să definiți un getter și un setter fără ștergere.

Dacă credeți că un atribut ar trebui setat numai atunci când este creată instanța sau că ar trebui modificat numai intern în cadrul clasei, puteți omite setatorul.

Puteți alege ce metode să includeți în funcție de contextul cu care lucrați.

🔸 În rezumat

  • Puteți defini proprietăți cu sintaxa @property, care este mai compactă și mai ușor de citit.
  • @property poate fi considerat modul „pitonic” de a defini getters, seters și ștergători.
  • Prin definirea proprietăților, puteți schimba implementarea internă a unei clase fără a afecta programul, astfel încât să puteți adăuga getters, setatori și ștergători care acționează ca intermediari „în culise” pentru a evita accesarea sau modificarea directă a datelor.

Sper cu adevărat că ți-a plăcut articolul meu și l-ai găsit util. Pentru a afla mai multe despre Proprietăți și programarea orientată pe obiecte în Python, vezi cursul meu online, care include peste 6 ore de prelegeri video, exerciții de codare și mini-proiecte.