Põhjalik ülevaade Pythoni enum-klassidest, võrreldes lippude enumeid funktsionaalse API lähenemisega robustsete ja paindlike enumeratsioonide jaoks.
Pythoni enum-klassid: lippude enumite vs. funktsionaalse API teostuse valdamine
Tarkvaraarenduses on selgus, hooldatavus ja töökindlus üliolulised. Pythoni enum
moodul pakub võimsa mehhanismi enumeratsioonitüüpide loomiseks, pakkudes struktureeritud ja väljendusrikast viisi sümbolnimede (liikmete) komplektide käsitlemiseks, mis on seotud unikaalsete, konstantsete väärtustega. Selle funktsioonide hulgas on lippude enumite ja funktsionaalse API kaudu loodud enumeratsioonide eristamine oluline arendajatele, kes soovivad Pythoni võimalusi maksimaalselt ära kasutada. See põhjalik juhend käsitleb mõlemat lähenemisviisi, tuues esile nende erinevused, kasutusjuhud, eelised ja võimalikud ohud globaalse publiku jaoks.
Pythoni enumeratsioonide mõistmine
Enne üksikasjadesse süvenemist loome põhjaliku arusaama Pythoni enum
moodulist. Python 3.4-s kasutusele võetud enumeratsioonid võimaldavad määratleda sümbolnimede (liikmete) komplekti, mis on unikaalsed ja konstantsed. See on eriti kasulik olukorras, kus peate esindama fikseeritud väärtuste komplekti, näiteks erinevaid olekuid, tüüpe või valikuid. Enumi kasutamine suurendab koodi loetavust ja vähendab vigade tekkimise tõenäosust, mis võivad tekkida toorete täisarvude või stringide kasutamisel.
Mõelge lihtsale näitele ilma enumiteta:
# Täisarvude kasutamine olekute esitamiseks
STATE_IDLE = 0
STATE_RUNNING = 1
STATE_PAUSED = 2
def process_state(state):
if state == STATE_RUNNING:
print("Töötlemine...")
elif state == STATE_PAUSED:
print("Pausil. Jätkamine...")
else:
print("Ootel.")
process_state(STATE_RUNNING)
Kuigi see töötab, on see vigadele vastuvõtlik. Mis siis, kui keegi kasutab kogemata 3
või kirjutab konstandi nagu STATE_RINING
valesti? Enumid leevendavad neid probleeme.
Siin on sama stsenaarium, kasutades põhilist enumi:
from enum import Enum
class State(Enum):
IDLE = 0
RUNNING = 1
PAUSED = 2
def process_state(state):
if state == State.RUNNING:
print("Töötlemine...")
elif state == State.PAUSED:
print("Pausil. Jätkamine...")
else:
print("Ootel.")
process_state(State.RUNNING)
See on loetavam ja turvalisem. Nüüd uurime kahte peamist viisi nende enumide määratlemiseks: funktsionaalne API ja lipu enumi lähenemine.
1. Funktsionaalse API teostus
Kõige otsesem viis enumeratsiooni loomiseks Pythonis on pärida enum.Enum
-ist ja määratleda liikmed klassi atribuutidena. Seda nimetatakse sageli klassipõhiseks süntaksiks. Kuid enum
moodul pakub ka funktsionaalset API-t, mis pakub dünaamilisemat viisi enumeratsioonide loomiseks, eriti siis, kui enumi definitsioon võidakse määrata käitusajal või kui vajate programmeerivamat lähenemist.
Funktsionaalsele API-le pääseb ligi Enum()
konstruktori kaudu. See võtab esimese argumendina enumi nime ja seejärel liikmete nimede jada või sõnastiku, mis kaardistab liikmete nimed nende väärtustele.
Funktsionaalse API sĂĽntaks
Funktsionaalse API ĂĽldine allkiri on:
Enum(väärtus, nimed, moodul=None, qualname=None, tüüp=None, start=1)
Kõige tavalisem kasutus hõlmab enumi nime ja nimede loendi või sõnastiku esitamist:
Näide 1: nimede loendi kasutamine
Kui esitate ainult nimede loendi, määratakse väärtused automaatselt alates 1-st (või määratud start
väärtusest).
from enum import Enum
# Funktsionaalse API kasutamine nimede loendiga
Color = Enum('Color', 'RED GREEN BLUE')
print(Color.RED)
print(Color.RED.value)
print(Color.GREEN.name)
# Väljund:
# Color.RED
# 1
# GREEN
Näide 2: nimede ja väärtuste sõnastiku kasutamine
Samuti saate esitada sõnastiku, et selgelt määratleda nii nimed kui ka nende vastavad väärtused.
from enum import Enum
# Funktsionaalse API kasutamine sõnastikuga
HTTPStatus = Enum('HTTPStatus', {
'OK': 200,
'NOT_FOUND': 404,
'INTERNAL_SERVER_ERROR': 500
})
print(HTTPStatus.OK)
print(HTTPStatus['NOT_FOUND'].value)
# Väljund:
# HTTPStatus.OK
# 404
Näide 3: tühikuga eraldatud nimede stringi kasutamine
Lihtsa enumide määratlemiseks on mugav viis edastada üks string tühikutega eraldatud nimedega.
from enum import Enum
# Funktsionaalse API kasutamine tĂĽhikutega eraldatud stringiga
Direction = Enum('Direction', 'NORTH SOUTH EAST WEST')
print(Direction.EAST)
print(Direction.SOUTH.value)
# Väljund:
# Direction.EAST
# 2
Funktsionaalse API eelised
- Dünaamiline loomine: Kasulik siis, kui enumeratsiooni liikmed või väärtused pole kompileerimise ajal teada, vaid määratakse käitusajal. See võib olla kasulik stsenaariumides, mis hõlmavad konfiguratsioonifaile või väliseid andmeallikaid.
- Lühidus: Lihtsate enumeratsioonide puhul võib see olla lühisem kui klassipõhine süntaks, eriti kui väärtused genereeritakse automaatselt.
- Programmeerimislik paindlikkus: Võimaldab enumide programmeerimisel genereerimist, mis võib olla kasulik metaprogrammeerimises või arenenud raamistiku arendamisel.
Millal kasutada funktsionaalset API-t
Funktsionaalne API on ideaalne olukordades, kus:
- Peate looma enumi dünaamiliste andmete põhjal.
- Te genereerite enumeid programmeerimisega osana suuremast sĂĽsteemist.
- Enum on väga lihtne ja ei vaja keerulisi käitumisi ega kohandusi.
2. Lippude enumid
Kuigi standardseid enumeratsioone on mõeldud eristuvate, vastastikku välistavate väärtuste jaoks, on lippude enumid spetsialiseerunud enumeratsioonitüüp, mis võimaldab mitme väärtuse kombinatsiooni. See saavutatakse pärides enum.Flag
-ist (mis ise pärib enum.Enum
-ist) ja tagades, et liikmete väärtused on kahendarvude astmed. See struktuur võimaldab bittide operatsioone (nt OR, AND, XOR) teha enumi liikmetel, võimaldades neil esindada lippude või õiguste komplekte.
Bittide operatsioonide jõud
Lippude enumide aluseks on see, et iga lippu saab esindada ühe bitiga täisarvus. Kahendarvude astmeid (1, 2, 4, 8, 16, ...) kasutades kaardistab iga enumi liige unikaalsele biti positsioonile.
Vaatame näidet failiõiguste abil, mis on lippude puhul levinud kasutusjuhtum.
from enum import Flag, auto
class FilePermissions(Flag):
READ = auto() # Väärtus on 1 (binaarne 0001)
WRITE = auto() # Väärtus on 2 (binaarne 0010)
EXECUTE = auto() # Väärtus on 4 (binaarne 0100)
OWNER = READ | WRITE | EXECUTE # Esindab kõiki omaniku õigusi
# Õiguste kontrollimine
user_permissions = FilePermissions.READ | FilePermissions.WRITE
print(user_permissions) # Väljund: FilePermissions.READ|WRITE
# Lippude seadistuse kontrollimine
print(FilePermissions.READ in user_permissions)
print(FilePermissions.EXECUTE in user_permissions)
# Väljund:
# True
# False
# Õiguste kombineerimine
all_permissions = FilePermissions.READ | FilePermissions.WRITE | FilePermissions.EXECUTE
print(all_permissions)
print(all_permissions == FilePermissions.OWNER)
# Väljund:
# FilePermissions.READ|WRITE|EXECUTE
# True
Selles näites:
auto()
määrab automaatselt järgmise saadaoleva kahendarvu astme igale liikmele.- Bittide OR-operaatorit (
|
) kasutatakse lippude kombineerimiseks. in
operaatorit (või&
operaatorit konkreetsete bittide kontrollimiseks) saab kasutada, et testida, kas konkreetne lipp või lippude kombinatsioon on suuremas komplektis olemas.
Lippude enumide määratlemine
Lippude enumeid määratletakse tavaliselt klassipõhise süntaksi abil, pärides enum.Flag
-ist.
Lippude enumide peamised omadused:
- Pärimine: Peab pärima
enum.Flag
-ist. - Kahendarvude astmete väärtused: Liikme väärtused peaksid ideaalis olema kahendarvude astmed. Funktsiooni
enum.auto()
on väga soovitatav kasutada, kuna see määrab automaatselt järjestikused kahendarvude astmed (1, 2, 4, 8, ...). - Bittide operatsioonid: Tugite bittide OR-i (
|
), AND-i (&
), XOR-i (^
) ja NOT-i (~
). - Liikmelisuse testimine:
in
operaator on ĂĽlekoormatud lihtsa lipu olemasolu kontrollimiseks.
Näide: veebiserveri õigused
Kujutage ette veebirakenduse ehitamist, kus kasutajatel on erinevad juurdepääsu tasemed. Lippude enumid sobivad selleks suurepäraselt.
from enum import Flag, auto
class WebPermissions(Flag):
NONE = 0
VIEW = auto() # 1
CREATE = auto() # 2
EDIT = auto() # 4
DELETE = auto() # 8
ADMIN = VIEW | CREATE | EDIT | DELETE # Kõik õigused
# Kasutaja vaatamis- ja redigeerimisõigustega
user_role = WebPermissions.VIEW | WebPermissions.EDIT
print(f"Kasutaja roll: {user_role}")
# Õiguste kontrollimine
if WebPermissions.VIEW in user_role:
print("Kasutaja saab sisu vaadata.")
if WebPermissions.DELETE in user_role:
print("Kasutaja saab sisu kustutada.")
else:
print("Kasutaja ei saa sisu kustutada.")
# Konkreetse kombinatsiooni kontrollimine
if user_role == (WebPermissions.VIEW | WebPermissions.EDIT):
print("Kasutajal on täpselt vaatamis- ja redigeerimisõigused.")
# Väljund:
# Kasutaja roll: WebPermissions.VIEW|EDIT
# Kasutaja saab sisu vaadata.
# Kasutaja ei saa sisu kustutada.
# Kasutajal on täpselt vaatamis- ja redigeerimisõigused.
Lippude enumide eelised
- Tõhus kombinatsioon: Võimaldab kombineerida mitut valikut üheks muutujaks, kasutades bittide operatsioone, mis on väga mälutõhusad.
- Selge esitus: Pakub selget ja inimloetavat viisi keerukate olekute või valikute komplektide esitamiseks.
- Töökindlus: Vähendab vigu võrreldes toorete bitimaskide kasutamisega, kuna enumi liikmed on nimetatud ja tüübikontrolliga.
- Intuitiivsed toimingud: Standardsete bittide operaatorite kasutamine muudab koodi intuitiivseks neile, kes on tuttavad bittide manipuleerimisega.
Millal kasutada lippude enumeid
Lippude enumid sobivad kõige paremini stsenaariumidele, kus:
- Peate esindama sõltumatute valikute komplekti, mida saab kombineerida.
- Tegelete bitimaskide, õiguste, režiimide või olekulipudega.
- Soovite nende valikute jaoks teha bittide operatsioone.
Lippude enumide ja funktsionaalse API võrdlemine
Kuigi mõlemad on Pythoni enum
mooduli võimsad tööriistad, teenivad need erinevaid eesmärke ja neid kasutatakse erinevates kontekstides.
Funktsioon | Funktsionaalne API | Lippude enumid |
---|---|---|
Peamine eesmärk | Standardsete enumeratsioonide dünaamiline loomine. | Kombineeritavate valikute (lippude) komplektide esitamine. |
Pärimine | enum.Enum |
enum.Flag |
Väärtuse määramine | Võib olla selge või automaatselt määratud täisarvud. | Tavaliselt kahendarvude astmed bittide operatsioonide jaoks; auto() on levinud. |
Põhitoimingud | Võrdluskontrollid, atribuutidele juurdepääs. | Bittide OR, AND, XOR, liikmelisuse testimine (in ). |
Kasutusjuhud | Fikseeritud eristatavate olekute, tüüpide, kategooriate komplektide määratlemine; dünaamiline enumi loomine. | Õigused, režiimid, sisse/välja lülitatavad valikud, bitimaskid. |
Süntaks | Enum('Name', 'member1 member2') või Enum('Name', {'M1': v1, 'M2': v2}) |
Klassipõhine definitsioon, mis pärib Flag -ist, kasutades sageli auto() ja bittide operaatoreid. |
Millal mitte kasutada lippude enumeid
Oluline on mõista, et lippude enumid on spetsialiseerunud. Te ei tohiks kasutada enum.Flag
, kui:
- Teie liikmed esindavad eristatavaid, vastastikku välistavaid valikuid (nt
State.RUNNING
jaState.PAUSED
ei tohiks kombineerida). Sellistel juhtudel on sobiv standardneenum.Enum
. - Te ei kavatse bittide operatsioone teha ega valikuid kombineerida.
- Teie väärtused ei ole loomulikult kahendarvude astmed või ei esinda bitte.
Millal mitte kasutada funktsionaalset API-t
Kuigi paindlik, ei pruugi funktsionaalne API olla parim valik, kui:
- Enumi definitsioon on staatiline ja teada arendamise ajal. Klassipõhine süntaks on staatiliste definitsioonide jaoks sageli loetavam ja hooldatavam.
- Peate oma enumi liikmetele lisama kohandatud meetodeid või keerukat loogikat. Klassipõhised enumid sobivad selleks paremini.
Globaalsed kaalutlused ja parimad tavad
Enumeratsioonidega rahvusvahelises kontekstis töötamisel tuleb arvesse võtta mitmeid tegureid:
1. Nimetamisreeglid ja rahvusvahelistumine (i18n)
Enumi liikmete nimed määratletakse tavaliselt inglise keeles. Kuigi Python ise ei toeta otseselt enumi *nimede* rahvusvahelistamist (need on identifikaatorid), saab nendega seotud *väärtusi* kasutada koos rahvusvahelistamise raamistikega.
Parim tava: Kasutage oma enumi liikmete jaoks selgeid, lühikesi ja ühemõttelisi inglisekeelseid nimesid. Kui need enumeratsioonid esindavad kasutajaliidesega seotud kontseptsioone, veenduge, et enumi väärtuste kaardistamist lokaliseeritud stringidele käsitletakse eraldi teie rakenduse rahvusvahelistamise kihis.
Näiteks, kui teil on enumi jaoks OrderStatus
:
from enum import Enum
class OrderStatus(Enum):
PENDING = 'PEN'
PROCESSING = 'PRC'
SHIPPED = 'SHP'
DELIVERED = 'DEL'
CANCELLED = 'CAN'
# Teie UI kihis (nt kasutades raamistikku nagu gettext):
# status_label = _(order_status.value) # See tooks lokaliseeritud stringi "PEN", "PRC" jne.
Lühikeste, järjepidevate stringiväärtuste (nt `'PEN'` for `PENDING`) kasutamine võib mõnikord lihtsustada lokaliseerimise otsingut võrreldes enumi liikme nimele tuginemisega.
2. Andmete serialiseerimine ja API-d
Kui saadate enumi väärtusi üle võrkude (nt REST API-des) või salvestate neid andmebaasidesse, vajate järjepidevat esitust. Enumi liikmed ise on objektid ja nende otsene serialiseerimine võib olla problemaatiline.
Parim tava: Serialiseerige alati oma enumi liikmete .value
. See pakub stabiilset, algelist tüüpi (tavaliselt täisarv või string), mida teised süsteemid ja keeled saavad hõlpsasti mõista.
Mõelge API lõpp-punktile, mis tagastab tellimuse üksikasjad:
import json
from enum import Enum
class OrderStatus(Enum):
PENDING = 1
PROCESSING = 2
SHIPPED = 3
class Order:
def __init__(self, order_id, status):
self.order_id = order_id
self.status = status
def to_dict(self):
return {
'order_id': self.order_id,
'status': self.status.value # Serialiseeri väärtus, mitte enumi liige
}
order = Order(123, OrderStatus.SHIPPED)
# Kui saadate JSONina:
print(json.dumps(order.to_dict()))
# Väljund: {"order_id": 123, "status": 3}
# Vastuvõtmise poolel:
# received_data = json.loads('{"order_id": 123, "status": 3}')
# received_status_value = received_data['status']
# actual_status_enum = OrderStatus(received_status_value) # Rekonstrueeri enum väärtusest
See lähenemine tagab koostalitlusvõime, kuna enamik programmeerimiskeeli saab täisarve või stringe hõlpsasti käsitleda. Andmete saamisel saate enumi liikme rekonstrueerida, kutsudes enumi klassi koos saadud väärtusega (nt OrderStatus(received_value)
).
3. Lippude enumi väärtused ja ühilduvus
Kui kasutate lippude enumeid väärtustega, mis on kahendarvude astmed, tagage järjepidevus. Kui suhtlete süsteemidega, mis kasutavad erinevaid bitimaske, võite vajada kohandatud kaardistamise loogikat. Siiski pakub enum.Flag
standardiseeritud viisi nende kombinatsioonide käsitlemiseks.
Parim tava: Kasutage lippude enumide jaoks enum.auto()
, välja arvatud juhul, kui teil on konkreetne põhjus määrata kohandatud kahendarvude astmeid. See tagab bittide määrangute õige ja järjepideva käsitsemise.
4. Jõudluse kaalutlused
Enamiku rakenduste puhul on jõudluse erinevus funktsionaalse API ja klassipõhiste definitsioonide või standardsete enumite ja lippude enumide vahel tühine. Pythoni enum
moodul on üldiselt tõhus. Kui aga loote käitusajal tohutul hulgal enumeid dünaamiliselt, võib funktsionaalsel API-l olla väike lisakulu võrreldes eelnevalt määratletud klassiga. Vastupidi, bittide operatsioonid lippude enumides on väga optimeeritud.
Täpsemad kasutusjuhud ja mustrid
1. Enumi käitumise kohandamine
Nii standardsetel kui ka lipu enumitel võivad olla kohandatud meetodid, mis võimaldavad teil lisada käitumist otse oma enumeratsioonidele.
from enum import Enum, auto
class TrafficLight(Enum):
RED = auto()
YELLOW = auto()
GREEN = auto()
def description(self):
if self == TrafficLight.RED:
return "Peatu! Punane tähendab ohtu."
elif self == TrafficLight.YELLOW:
return "Ettevaatust! Valmistuge peatumiseks või jätkake ettevaatlikult."
elif self == TrafficLight.GREEN:
return "Mine! Roheline tähendab, et on ohutu jätkata."
return "Tundmatu olek."
print(TrafficLight.RED.description())
print(TrafficLight.GREEN.description())
# Väljund:
# Peatu! Punane tähendab ohtu.
# Mine! Roheline tähendab, et on ohutu jätkata.
2. Enumi liikmete iteratsioon ja otsing
Saate itereerida enumi kõiki liikmeid ja otsinguid teha nime või väärtuse järgi.
from enum import Enum
class UserRole(Enum):
GUEST = 'guest'
MEMBER = 'member'
ADMIN = 'admin'
# Liikmete itereerimine
print("Kõik rollid:")
for role in UserRole:
print(f" - {role.name}: {role.value}")
# Otsing nime järgi
admin_role_by_name = UserRole['ADMIN']
print(f"Otsing nimega 'ADMIN': {admin_role_by_name}")
# Otsing väärtuse järgi
member_role_by_value = UserRole('member')
print(f"Otsing väärtusega 'member': {member_role_by_value}")
# Väljund:
# Kõik rollid:
# - GUEST: guest
# - MEMBER: member
# - ADMIN: admin
# Otsing nimega 'ADMIN': UserRole.ADMIN
# Otsing väärtusega 'member': UserRole.MEMBER
3. Enumi kasutamine koos andmeklasside või Pydanticiga
Enumid integreeruvad sujuvalt kaasaegsete Pythoni andmestruktuuridega, nagu andmeklassid ja valideerimisteegid nagu Pydantic, pakkudes tĂĽĂĽbiturvalisust ja selget andmete esitust.
from dataclasses import dataclass
from enum import Enum
class Priority(Enum):
LOW = 1
MEDIUM = 2
HIGH = 3
@dataclass
class Task:
name: str
priority: Priority
task1 = Task("Blogipostituse kirjutamine", Priority.HIGH)
print(task1)
# Väljund:
# Task(name='Blogipostituse kirjutamine', priority=Priority.HIGH)
Pydantic kasutab enumeid tugeva andmete valideerimise jaoks. Kui Pydantici mudeli väli on enumi tüüp, käsitleb Pydantic automaatselt teisendamist toorväärtustest (nt täisarvudest või stringidest) õigeks enumi liikmeks.
Kokkuvõte
Pythoni enum
moodul pakub võimsaid tööriistu sümbolkonstantide haldamiseks. Funktsionaalse API ja lippude enumide erinevuse mõistmine on tõhusa ja hooldatava Pythoni koodi kirjutamise võti.
- Kasutage funktsionaalset API-t siis, kui peate enumeratsioone dünaamiliselt looma või väga lihtsate, staatiliste definitsioonide jaoks, kus on prioriteediks lühidus.
- Rakendage lippude enumeid, kui peate esindama kombineeritavaid valikuid, õigusi või bitimaske, kasutades bittide operatsioonide jõudu tõhusaks ja selgeks olekuhalduseks.
Valides hoolikalt sobiva enumeratsiooni strateegia ning järgides parimaid tavasid nimetamisel, serialiseerimisel ja rahvusvahelistamisel, saavad arendajad kogu maailmas suurendada oma Pythoni rakenduste selgust, ohutust ja koostalitlusvõimet. Ükskõik, kas loote ülemaailmset e-kaubanduse platvormi, keerukat taustateenust või lihtsat utiliidi skripti, aitab Pythoni enumide valdamine kahtlemata kaasa usaldusväärsema ja arusaadavama koodi loomisele.
Pidage meeles: Eesmärk on muuta teie kood võimalikult loetavaks ja vigadele vastupidavaks. Enumid oma erinevatel kujudel on asendamatud vahendid selle eesmärgi saavutamisel. Hinnake pidevalt oma vajadusi ja valige enumi teostus, mis kõige paremini sobib käsiloleva probleemiga.