Õppige Pythoni omadusdeskriptoreid arvutatud omaduste, atribuutide valideerimise ja täiustatud objektorienteeritud disaini jaoks. Õppige praktiliste näidete ja parimate tavade abil.
Pythoni omadusdeskriptorid: arvutatud omadused ja valideerimisloogika
Pythoni omadusdeskriptorid pakuvad võimsa mehhanismi atribuutidele juurdepääsu ja käitumise haldamiseks klassides. Need võimaldavad teil määratleda kohandatud loogika atribuutide hankimiseks, määramiseks ja kustutamiseks, võimaldades teil luua arvutatud omadusi, jõustada valideerimisreegleid ja rakendada täiustatud objektorienteeritud disainimustreid. See põhjalik juhend uurib omadusdeskriptorite plusse ja miinuseid, pakkudes praktilisi näiteid ja parimaid tavasid, et aidata teil seda olulist Pythoni funktsiooni omandada.
Mis on omadusdeskriptorid?
Pythonis on deskriptor objektatribuut, millel on "siduv käitumine", mis tähendab, et atribuudi juurdepääs on deskriptori protokollis olevate meetoditega tühistatud. Need meetodid on __get__()
, __set__()
ja __delete__()
. Kui mõni neist meetoditest on atribuudi jaoks määratletud, saab sellest deskriptor. Eelkõige on omadusdeskriptorid spetsiifiline deskriptori tüüp, mis on loodud atribuutidele juurdepääsu haldamiseks kohandatud loogikaga.
Deskriptorid on madala taseme mehhanism, mida kasutavad kulisside taga paljud sisseehitatud Pythoni funktsioonid, sealhulgas omadused, meetodid, staatilised meetodid, klassimeetodid ja isegi super()
. Deskriptorite mõistmine annab teile võimaluse kirjutada keerukamat ja Pythonic koodi.
Deskriptori protokoll
Deskriptori protokoll määratleb meetodid, mis kontrollivad atribuutidele juurdepääsu:
__get__(self, instance, owner)
: kutsutakse, kui deskriptori väärtus on saadud.instance
on deskriptorit sisaldava klassi eksemplar jaowner
on klass ise. Kui deskriptorile pääsetakse juurde klassist (ntMyClass.my_descriptor
), oninstance
väärtusNone
.__set__(self, instance, value)
: kutsutakse, kui deskriptori väärtus on määratud.instance
on klassi eksemplar javalue
on määratav väärtus.__delete__(self, instance)
: kutsutakse, kui deskriptori atribuut kustutatakse.instance
on klassi eksemplar.
Oma omadusdeskriptori loomiseks peate määratlema klassi, mis rakendab vähemalt ühte neist meetoditest. Alustame lihtsa näitega.
Põhilise omadusdeskriptori loomine
Siin on põhinäide omadusdeskriptorist, mis teisendab atribuudi suurtähtedeks:
class UppercaseDescriptor:
def __get__(self, instance, owner):
if instance is None:
return self # Tagastage deskriptor ise, kui sellele pääseb juurde klassist
return instance._my_attribute.upper() # Juurdepääs "privaatsele" atribuudile
def __set__(self, instance, value):
instance._my_attribute = value
class MyClass:
my_attribute = UppercaseDescriptor()
def __init__(self, value):
self._my_attribute = value # Initsialiseerige "privaatne" atribuut
# Näide kasutamisest
obj = MyClass("hello")
print(obj.my_attribute) # Väljund: HELLO
obj.my_attribute = "world"
print(obj.my_attribute) # Väljund: WORLD
Selles näites:
UppercaseDescriptor
on deskriptorklass, mis rakendab__get__()
ja__set__()
.MyClass
määratleb atribuudimy_attribute
, mis onUppercaseDescriptor
eksemplar.- Kui pääsete juurde
obj.my_attribute
, kutsutakseUppercaseDescriptor
meetodit__get__()
, teisendades aluseks oleva_my_attribute
suurtähtedeks. - Kui määrate
obj.my_attribute
, kutsutakse meetodit__set__()
, värskendades aluseks olevat_my_attribute
.
Pange tähele "privaatse" atribuudi (_my_attribute
) kasutamist. See on Pythonis tavaline tava, mis näitab, et atribuut on mõeldud sisemiseks kasutamiseks klassis ja sellele ei tohiks väljastpoolt otse juurde pääseda. Deskriptorid annavad meile mehhanismi nendele "privaatsetele" atribuutidele juurdepääsu vahendamiseks.
Arvutatud omadused
Omadusdeskriptorid sobivad suurepäraselt arvutatud omaduste loomiseks – atribuudid, mille väärtused arvutatakse dünaamiliselt teiste atribuutide põhjal. See aitab hoida teie andmed järjepidevad ja koodi paremini hallatavana. Vaatleme näidet, mis hõlmab valuuta konverteerimist (kasutades näitlikustamiseks hüpoteetilisi konverteerimiskursse):
class CurrencyConverter:
def __init__(self, usd_to_eur_rate, usd_to_gbp_rate):
self.usd_to_eur_rate = usd_to_eur_rate
self.usd_to_gbp_rate = usd_to_gbp_rate
class Money:
def __init__(self, usd, converter):
self.usd = usd
self.converter = converter
class EURDescriptor:
def __get__(self, instance, owner):
if instance is None:
return self
return instance.usd * instance.converter.usd_to_eur_rate
def __set__(self, instance, value):
raise AttributeError("EUR-i ei saa otse määrata. Määrake selle asemel USD.")
class GBPDescriptor:
def __get__(self, instance, owner):
if instance is None:
return self
return instance.usd * instance.converter.usd_to_gbp_rate
def __set__(self, instance, value):
raise AttributeError("GBP-d ei saa otse määrata. Määrake selle asemel USD.")
eur = EURDescriptor()
gbp = GBPDescriptor()
# Näide kasutamisest
converter = CurrencyConverter(0.85, 0.75) # USD kuni EUR ja USD kuni GBP kurss
money = Money(100, converter)
print(f"USD: {money.usd}")
print(f"EUR: {money.eur}")
print(f"GBP: {money.gbp}")
# Katse määrata EUR või GBP tõstab esile AttributeError
# money.eur = 90 # See tekitab vea
Selles näites:
CurrencyConverter
sisaldab konverteerimiskursse.Money
esindab rahasummat USD-des ja viitabCurrencyConverter
eksemplarile.EURDescriptor
jaGBPDescriptor
on deskriptorid, mis arvutavad EUR ja GBP väärtused USD väärtuse ja konverteerimiskursside põhjal.- Atribuudid
eur
jagbp
on nende deskriptorite eksemplarid. - Meetodid
__set__()
tõstavad esileAttributeError
, et vältida arvutatud EUR ja GBP väärtuste otsest muutmist. See tagab, et muudatused tehakse USD väärtuse kaudu, säilitades järjepidevuse.
Atribuutide valideerimine
Omadusdeskriptoreid saab kasutada ka atribuudi väärtuste valideerimisreeglite jõustamiseks. See on ülioluline andmete terviklikkuse tagamiseks ja vigade ennetamiseks. Loome deskriptori, mis valideerib e-posti aadresse. Hoiame valideerimise näite jaoks lihtsana.
import re
class EmailDescriptor:
def __init__(self, attribute_name):
self.attribute_name = attribute_name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.attribute_name]
def __set__(self, instance, value):
if not self.is_valid_email(value):
raise ValueError(f"Vigane e-posti aadress: {value}")
instance.__dict__[self.attribute_name] = value
def __delete__(self, instance):
del instance.__dict__[self.attribute_name]
def is_valid_email(self, email):
# Lihtne e-posti valideerimine (saab parandada)
pattern = r"^[\w\.-]+@([\w-]+\.)+[\w-]{2,4}$"
return re.match(pattern, email) is not None
class User:
email = EmailDescriptor("email")
def __init__(self, email):
self.email = email
# Näide kasutamisest
user = User("test@example.com")
print(user.email)
# Katse määrata vigane e-post tekitab ValueError
# user.email = "invalid-email" # See tekitab vea
try:
user.email = "invalid-email"
except ValueError as e:
print(e)
Selles näites:
EmailDescriptor
valideerib e-posti aadressi regulaaravaldise abil (is_valid_email
).- Meetod
__set__()
kontrollib, kas väärtus on enne selle määramist kehtiv e-post. Kui ei, siis tõstab see esileValueError
. - Klass
User
kasutabEmailDescriptor
atribuudiemail
haldamiseks. - Deskriptor salvestab väärtuse otse eksemplari
__dict__
, mis võimaldab juurdepääsu ilma deskriptorit uuesti käivitamata (vältides lõpmatut rekursiooni).
See tagab, et atribuudile email
saab määrata ainult kehtivaid e-posti aadresse, suurendades andmete terviklikkust. Pange tähele, et funktsioon is_valid_email
pakub ainult põhilist valideerimist ja seda saab täiustada robustsemate kontrollide jaoks, kasutades vajadusel rahvusvahelistatud e-posti valideerimiseks väliseid teeke.
Sisseehitatud funktsiooni `property` kasutamine
Python pakub sisseehitatud funktsiooni nimega property()
, mis lihtsustab lihtsate omadusdeskriptorite loomist. See on sisuliselt mugav ümbris deskriptori protokolli ümber. Seda eelistatakse sageli põhiliste arvutatud omaduste puhul.
class Rectangle:
def __init__(self, width, height):
self._width = width
self._height = height
def get_area(self):
return self._width * self._height
def set_area(self, area):
# Rakendage loogika laiuse/kõrguse arvutamiseks alalt
# Lihtsuse huvides seame lihtsalt laiuse ja kõrguse ruutjuureks
import math
side = math.sqrt(area)
self._width = side
self._height = side
def delete_area(self):
self._width = 0
self._height = 0
area = property(get_area, set_area, delete_area, "RistkĂĽliku pindala")
# Näide kasutamisest
rect = Rectangle(5, 10)
print(rect.area) # Väljund: 50
rect.area = 100
print(rect._width) # Väljund: 10.0
print(rect._height) # Väljund: 10.0
del rect.area
print(rect._width) # Väljund: 0
print(rect._height) # Väljund: 0
Selles näites:
property()
võtab vastu kuni neli argumenti:fget
(getter),fset
(setter),fdel
(deleter) jadoc
(docstring).- Määratleme eraldi meetodid
area
hankimiseks, määramiseks ja kustutamiseks. property()
loob omadusdeskriptori, mis kasutab neid meetodeid atribuutidele juurdepääsu haldamiseks.
Sisseehitatud property
on lihtsate juhtumite puhul sageli loetavam ja lühem kui eraldi deskriptorklassi loomine. Kuid keerukama loogika korral või kui peate deskriptoriloogikat taaskasutama mitmes atribuudis või klassis, pakub kohandatud deskriptorklassi loomine paremat korraldust ja taaskasutatavust.
Millal kasutada omadusdeskriptoreid
Omadusdeskriptorid on võimas tööriist, kuid neid tuleks kasutada mõistlikult. Siin on mõned stsenaariumid, kus need on eriti kasulikud:
- Arvutatud omadused: Kui atribuudi väärtus sõltub teistest atribuutidest või välistest teguritest ja seda tuleb dünaamiliselt arvutada.
- Atribuutide valideerimine: Kui peate andmete terviklikkuse säilitamiseks jõustama atribuutide väärtustele konkreetsed reeglid või piirangud.
- Andmekapseldus: Kui soovite juhtida atribuutidele juurdepääsu ja nende muutmise viisi, peites aluseks olevad rakenduse üksikasjad.
- Kirjutuskaitstud atribuudid: Kui soovite vältida atribuudi muutmist pärast selle initsialiseerimist (määrates ainult meetodi
__get__
). - Laia laadimine: Kui soovite atribuudi väärtuse laadida alles siis, kui sellele esimest korda juurde pääsetakse (nt andmete laadimine andmebaasist).
- Integreerimine väliste süsteemidega: Deskriptoreid saab kasutada abstraktsioonikihina teie objekti ja välise süsteemi (nt andmebaasi/API) vahel, et teie rakendus ei peaks muretsema aluseks oleva esituse pärast. See suurendab teie rakenduse teisaldatavust. Kujutage ette, et teil on atribuut, mis salvestab kuupäeva, kuid aluseks olev salvestusruum võib olenevalt platvormist olla erinev, võite selle abstraheerimiseks kasutada deskriptorit.
Kuid vältige omadusdeskriptorite tarbetut kasutamist, kuna need võivad teie koodi keerukamaks muuta. Lihtsa atribuudile juurdepääsu korral ilma spetsiaalse loogikata piisab sageli otsesest atribuudile juurdepääsust. Deskriptorite ülekasutamine võib muuta teie koodi raskemini mõistetavaks ja hallatavaks.
Parimad tavad
Siin on mõned parimad tavad, mida omadusdeskriptoritega töötamisel meeles pidada:
- Kasutage "privaatseid" atribuute: Salvestage aluseks olevad andmed "privaatsetesse" atribuutidesse (nt
_my_attribute
), et vältida nimede konflikte ja vältida otsest juurdepääsu väljastpoolt klassi. - Käsitsege
instance is None
: Meetodis__get__()
käsitlege juhtumit, kuiinstance
onNone
, mis juhtub siis, kui deskriptorile pääsetakse juurde pigem klassist endast kui eksemplarist. Sel juhul tagastage deskriptoriobjekt ise. - Tõstke esile sobivad erandid: Kui valideerimine nurjub või kui atribuudi määramine pole lubatud, tõstke esile sobivad erandid (nt
ValueError
,TypeError
,AttributeError
). - Dokumenteerige oma deskriptorid: Lisage oma deskriptorklassidele ja omadustele dokumendistringid, et selgitada nende eesmärki ja kasutamist.
- Arvestage jõudlusega: Keerukas deskriptoriloogika võib mõjutada jõudlust. Profileerige oma koodi, et tuvastada jõudluse kitsaskohad ja optimeerida vastavalt oma deskriptoreid.
- Valige õige lähenemisviis: Otsustage, kas kasutada sisseehitatud
property
või kohandatud deskriptorklassi, lähtudes loogika keerukusest ja vajadusest taaskasutatavuse järele. - Hoidke see lihtsana: Sarnaselt mis tahes muu koodiga tuleks vältida keerukust. Deskriptorid peaksid parandama teie disaini kvaliteeti, mitte seda varjama.
Täiustatud deskriptoritehnikad
Lisaks põhiteadmistele saab omadusdeskriptoreid kasutada täiustatud tehnikate jaoks:
- Mitteandmedeskriptorid: Deskriptoreid, mis määratlevad ainult meetodi
__get__()
, nimetatakse mitteandmedeskriptoriteks (või mõnikord ka "varjustavateks" deskriptoriteks). Neil on madalam prioriteet kui eksemplari atribuutidel. Kui on olemas sama nimega eksemplari atribuut, varjab see mitteandmedeskriptorit. See võib olla kasulik vaikeväärtuste pakkumiseks või laisa laadimise käitumiseks. - Andmedeskriptorid: Deskriptoreid, mis määratlevad
__set__()
või__delete__()
, nimetatakse andmedeskriptoriteks. Neil on kõrgem prioriteet kui eksemplari atribuutidel. Atribuudile juurdepääs või sellele määramine käivitab alati deskriptori meetodid. - Deskriptorite kombineerimine: Keerukama käitumise loomiseks saate kombineerida mitu deskriptorit. Näiteks võiks teil olla deskriptor, mis nii valideerib kui ka teisendab atribuuti.
- Metaklassid: Deskriptorid suhtlevad võimsalt metaklassidega, kus omadused määratakse metaklassi poolt ja pärivad need klassid, mille see loob. See võimaldab äärmiselt võimsat disaini, muutes deskriptorid klassides taaskasutatavaks ja isegi automatiseerides deskriptorite määramist metaandmete põhjal.
Globaalsed kaalutlused
Omadusdeskriptoritega kujundamisel, eriti globaalses kontekstis, pidage meeles järgmist:
- Lokaliseerimine: Kui valideerite lokaadist sõltuvaid andmeid (nt postiindeksid, telefoninumbrid), kasutage sobivaid teeke, mis toetavad erinevaid piirkondi ja vorminguid.
- Ajavööndid: Kuupäevade ja kellaaegadega töötamisel olge ajavöönditega ettevaatlik ja kasutage teisenduste õigeks käsitsemiseks teeke, nagu
pytz
. - Valuuta: Kui tegelete valuutaväärtustega, kasutage teeke, mis toetavad erinevaid vääringuid ja vahetuskursse. Kaaluge standardse valuutavormingu kasutamist.
- Tegelaskodeering: Veenduge, et teie kood käsitleks erinevaid tegelaskodeeringuid õigesti, eriti stringide valideerimisel.
- Andmete valideerimise standardid: Mõnel piirkonnal on konkreetsed juriidilised või regulatiivsed andmete valideerimise nõuded. Olge nendest teadlik ja veenduge, et teie deskriptorid vastavad neile.
- Juurdepääsetavus: Omadused tuleks kujundada nii, et teie rakendus saaks kohaneda erinevate keelte ja kultuuridega ilma põhidisaini muutmata.
Järeldus
Pythoni omadusdeskriptorid on võimas ja mitmekülgne tööriist atribuutidele juurdepääsu ja käitumise haldamiseks. Need võimaldavad teil luua arvutatud omadusi, jõustada valideerimisreegleid ja rakendada täiustatud objektorienteeritud disainimustreid. Mõistes deskriptoriprotokolli ja järgides parimaid tavasid, saate kirjutada keerukamat ja paremini hallatavat Pythoni koodi.
Alates andmete terviklikkuse tagamisest valideerimisega kuni tuletatud väärtuste arvutamiseni nõudmisel pakuvad omadusdeskriptorid elegantse viisi atribuutide käsitsemise kohandamiseks teie Pythoni klassides. Selle funktsiooni valdamine avab Pythoni objektimudeli sügavama mõistmise ja annab teile võimaluse luua töökindlamaid ja paindlikumaid rakendusi.
Kasutades property
või kohandatud deskriptoreid, saate oma Pythoni oskusi oluliselt parandada.