Enum Modülü: Sabit Değerlerin Zarif Yönetimi
Giriş
Programlama yaparken sık sık sabit değerlerle çalışırız: haftanın günleri, HTTP durum kodları, sipariş durumları, renk seçenekleri... Çoğu geliştirici alışkanlıkla status = 1, color = "red" gibi sıradan değişkenler kullanır. Ancak bu yaklaşım büyük projelerde ciddi sorunlara yol açar.
Python'un enum modülü tam da bu sorunu çözmek için tasarlanmıştır. Bu derste Enum, IntEnum, StrEnum, Flag, auto() ve @unique araçlarını gerçek örneklerle inceleyeceğiz.
1. Neden Enum Kullanmalıyız? — Magic Number Problemi
Şu kodu bir düşünün:
def process_order(order):
if order.status == 0:
print("Beklemede")
elif order.status == 1:
print("Ödendi")
elif order.status == 2:
print("Kargoya verildi")
elif order.status == 3:
print("Teslim edildi")Bu kodda 0, 1, 2, 3 rakamları magic number (sihirli sayı) olarak adlandırılır. Kodu okuyan biri "2 ne demek?" diye sormak zorunda kalır. Üstelik yanlışlıkla order.status = 99 yazarsanız Python hiçbir hata vermez — hata runtime'da sessizce kaybolur.
Bir de string sabitleri deneyelim:
STATUS_PENDING = "pending"
STATUS_PAID = "paid"
STATUS_SHIPPED = "shipped"
def process_order(order):
if order.status == STATUS_PENDING:
print("Beklemede")
elif order.status == "piad": # typo! Kimse fark etmez
print("Ödendi")String sabitleri biraz daha okunabilir ama typo (yazım hatası) riski devam eder. "piad" yazdığınızda Python yine sessiz kalır. Ayrıca bu sabitler arasında mantıksal bir bağlantı yoktur — hepsi bağımsız değişkenlerdir.
Enum tam da bu noktada devreye girer. Sabit değerleri bir grup altında toplar, tip güvenliği sağlar ve okunabilirliği artırır.
from enum import Enum
class OrderStatus(Enum):
PENDING = 0
PAID = 1
SHIPPED = 2
DELIVERED = 3
def process_order(order):
if order.status == OrderStatus.PENDING:
print("Beklemede")
elif order.status == OrderStatus.PAID:
print("Ödendi")Artık OrderStatus.PIAD yazmaya çalışırsanız Python anında AttributeError fırlatır. Magic number'lar gitti, yerine anlamlı isimler geldi. Kodunuz hem okunabilir hem güvenli.
2. Temel Enum Tanımlama
Bir enum tanımlamak için enum modülünden Enum sınıfını import edip ondan türetme yaparsınız:
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
YELLOW = 4Bu kadar basit. Color artık dört üyesi olan bir enum tipidir. Her üye (RED, GREEN, BLUE, YELLOW) hem bir isme hem de bir değere sahiptir.
Trafik Lambası Analojisi 🚦
Enum'u anlamanın en güzel yolu trafik lambasını düşünmektir. Bir trafik lambası yalnızca üç durumdan birinde olabilir: kırmızı, sarı veya yeşil. Dördüncü bir durum yoktur. Aynı anda iki durumda olamaz (Flag hariç, ona geleceğiz). Ve "biraz kırmızımsı" diye bir şey söz konusu değildir — ya kırmızıdır ya değildir.
from enum import Enum
class TrafficLight(Enum):
RED = "dur"
YELLOW = "hazırlan"
GREEN = "geç"
current = TrafficLight.RED
print(current) # TrafficLight.RED
print(current.value) # dur
print(current.name) # REDİşte enum'lar tam olarak budur: sonlu, sabit, birbirinden farklı durumlar kümesi. Trafik lambası gibi — ne eksik ne fazla, tam olarak tanımladığınız kadar durum vardır.
Enum üyeleri singleton (tekil) nesnelerdir. Yani TrafficLight.RED her zaman aynı nesneyi ifade eder. Bu, karşılaştırmaları hem güvenli hem hızlı kılar.
3. Enum Üyeleri: name ve value Erişimi
Her enum üyesinin iki temel özelliği vardır: name (isim) ve value (değer).
from enum import Enum
class Planet(Enum):
MERCURY = 1
VENUS = 2
EARTH = 3
MARS = 4
planet = Planet.EARTH
print(planet.name) # EARTH (string)
print(planet.value) # 3 (int)
print(type(planet)) # <enum 'Planet'>name her zaman bir string'dir ve enum üyesinin tanımlandığı ismi döndürür. value ise atadığınız değerdir — int, string, tuple, hatta başka bir nesne olabilir.
Değerden Enum'a Erişim
Elinizde bir değer varsa ve bunu enum üyesine dönüştürmek istiyorsanız, enum sınıfını çağırmanız yeterlidir:
from enum import Enum
class HttpStatus(Enum):
OK = 200
NOT_FOUND = 404
SERVER_ERROR = 500
# Değerden enum üyesine
status = HttpStatus(404)
print(status) # HttpStatus.NOT_FOUND
print(status.name) # NOT_FOUND
# İsimden enum üyesine (köşeli parantez)
status2 = HttpStatus["OK"]
print(status2) # HttpStatus.OK
print(status2.value) # 200HttpStatus(404) yazdığınızda Python, value'su 404 olan üyeyi bulur ve döndürür. Eğer böyle bir değer yoksa ValueError fırlatır. HttpStatus["OK"] ise isme göre erişim sağlar — bulunamazsa KeyError alırsınız.
💡 İpucu: API'den gelen verileri enum'a çevirirken
try/exceptkullanmayı unutmayın. Olmayan bir değer içinHttpStatus(999)→ValueError, olmayan bir isim içinHttpStatus["TEAPOT"]→KeyErroralırsınız.
4. Enum Iteration ve Karşılaştırma
Enum sınıfları iterable'dır (üzerinde döngü kurulabilir). Bu, tüm üyeleri listelemek veya bir seçenek menüsü oluşturmak için çok kullanışlıdır.
from enum import Enum
class Season(Enum):
SPRING = 1
SUMMER = 2
AUTUMN = 3
WINTER = 4
# Tüm üyeler üzerinde döngü
for season in Season:
print(f"{season.name} = {season.value}")
# SPRING = 1
# SUMMER = 2
# AUTUMN = 3
# WINTER = 4
# Üye listesi
print(list(Season))
# [<Season.SPRING: 1>, <Season.SUMMER: 2>, ...]Karşılaştırma: is ve ==
Enum üyeleri hem is (identity) hem == (equality) ile karşılaştırılabilir. Ancak sıralama operatörleri (<, >, <=, >=) standart Enum için desteklenmez.
from enum import Enum
class Direction(Enum):
NORTH = "N"
SOUTH = "S"
EAST = "E"
WEST = "W"
d = Direction.NORTH
# Identity karşılaştırma (önerilen)
print(d is Direction.NORTH) # True
print(d is Direction.SOUTH) # False
# Equality karşılaştırma
print(d == Direction.NORTH) # True
print(d == "N") # False! Enum != stringDikkat edin: Direction.NORTH == "N" ifadesi False döndürür. Enum üyeleri kendi değerleriyle (value) doğrudan karşılaştırılmaz — bu bilinçli bir tasarım kararıdır ve tip güvenliğini korur.
⚠️ Dikkat: Standart
Enumsınıfında<,>gibi sıralama operatörleriTypeErrorverir. Sıralama yapmanız gerekiyorsa yaIntEnumkullanın ya dasorted(items, key=lambda x: x.value)ile sıralayın.
5. IntEnum — Sayısal Enum
IntEnum, int ile Enum'un birleşimidir. Üyeleri gerçek birer integer gibi davranır — aritmetik ve karşılaştırma operatörleri çalışır.
from enum import IntEnum
class Priority(IntEnum):
LOW = 1
MEDIUM = 2
HIGH = 3
CRITICAL = 4
# Sıralama operatörleri çalışır
print(Priority.LOW < Priority.HIGH) # True
print(Priority.CRITICAL >= Priority.HIGH) # True
# Integer ile karşılaştırma da çalışır
print(Priority.MEDIUM == 2) # True
print(Priority.HIGH > 2) # TrueIntEnum üyeleri integer ile doğrudan karşılaştırılabilir çünkü int'ten türetilmiştir. Bu özellik veritabanı veya API entegrasyonlarında çok işe yarar.
from enum import IntEnum
class ExitCode(IntEnum):
SUCCESS = 0
ERROR = 1
PERMISSION_DENIED = 2
NOT_FOUND = 127
# Matematiksel işlemlerde kullanılabilir
code = ExitCode.ERROR
print(code + 10) # 11 (int döner, enum değil!)
print(type(code + 10)) # <class 'int'>
# Fonksiyonlara int olarak geçirilebilir
import sys
# sys.exit(ExitCode.SUCCESS) # 0 ile çıkarAncak bir uyarı: aritmetik işlemlerin sonucu int döner, IntEnum değil. Yani ExitCode.ERROR + 10 ifadesinin tipi int'tir.
⚠️ Dikkat:
IntEnumkullanırken tip güvenliğinden ödün verirsiniz.Priority.HIGH == 3ifadesiTruedöner — bu bazen istenmeyen karşılaştırmalara yol açabilir. Eğer sıralama gerekmiyorsa standartEnumtercih edin.
6. StrEnum (Python 3.11+) — String Bazlı Enum
Python 3.11 ile gelen StrEnum, IntEnum'un string versiyonudur. Üyeleri gerçek birer string gibi davranır.
from enum import StrEnum
class Color(StrEnum):
RED = "red"
GREEN = "green"
BLUE = "blue"
# String ile doğrudan karşılaştırma
print(Color.RED == "red") # True
print(Color.GREEN.upper()) # GREEN
# String fonksiyonlarında kullanılabilir
print(f"Selected: {Color.BLUE}") # Selected: blue
print("red" in [c.value for c in Color]) # TrueStrEnum özellikle API response'ları, JSON key'leri ve konfigürasyon değerleri için idealdir. String ile doğrudan karşılaştırılabilmesi entegrasyonları kolaylaştırır.
from enum import StrEnum
class Environment(StrEnum):
DEV = "development"
STAGING = "staging"
PROD = "production"
# JSON verisinden gelen string ile eşleştirme
config_env = "production" # API'den geldi
if config_env == Environment.PROD:
print("Dikkat! Production ortamındasınız!")
# String concatenation çalışır
env = Environment.DEV
print("Running in " + env) # Running in developmentPython 3.11 öncesi için StrEnum yoktur ama class MyStrEnum(str, Enum) şeklinde çoklu kalıtım (multiple inheritance) yaparak aynı davranışı elde edebilirsiniz.
7. Flag ve IntFlag — Bitwise Combinable Enum
Bazen bir nesnenin aynı anda birden fazla durumda olması gerekir. Örneğin bir dosyanın hem okunabilir hem yazılabilir olması, veya bir kullanıcının hem admin hem moderatör yetkisine sahip olması. İşte Flag ve IntFlag bu senaryolar için tasarlanmıştır.
from enum import Flag, auto
class Permission(Flag):
READ = auto() # 1
WRITE = auto() # 2
EXECUTE = auto() # 4
DELETE = auto() # 8
# Birden fazla izin birleştirilebilir (bitwise OR)
user_perms = Permission.READ | Permission.WRITE
print(user_perms) # Permission.READ|WRITE
print(Permission.READ in user_perms) # True
print(Permission.DELETE in user_perms) # FalseFlag üyelerinin değerleri 2'nin kuvvetleri olmalıdır (1, 2, 4, 8...). auto() bunu otomatik olarak yapar. Birleştirme | (OR) operatörüyle, kontrol in anahtar kelimesiyle yapılır.
IntFlag — Integer ile Uyumlu Flag
IntFlag, Flag'in integer ile uyumlu versiyonudur. Tıpkı IntEnum gibi, integer değerlerle doğrudan karşılaştırma ve aritmetik yapabilirsiniz.
from enum import IntFlag, auto
class UnixPermission(IntFlag):
NONE = 0
EXECUTE = 1 # 001
WRITE = 2 # 010
READ = 4 # 100
# Klasik Unix izin grupları
RW = READ | WRITE # 6 (110)
RX = READ | EXECUTE # 5 (101)
RWX = READ | WRITE | EXECUTE # 7 (111)
# Integer ile karşılaştırma
perms = UnixPermission(6) # RW
print(perms) # UnixPermission.READ|WRITE
print(UnixPermission.READ in perms) # True
print(perms == 6) # True
# chmod 755 gibi düşünün
owner = UnixPermission(7) # RWX
group = UnixPermission(5) # RX
others = UnixPermission(5) # RX
print(f"Owner: {owner}, Group: {group}, Others: {others}")Flag ve IntFlag arasındaki fark, Enum ve IntEnum arasındaki farkla aynıdır: IntFlag integer ile doğrudan karşılaştırılabilir, Flag ise yalnızca kendi üyeleriyle karşılaştırılır.
8. auto() ile Otomatik Değer Atama
Her enum üyesine elle değer atamak bazen gereksizdir. Özellikle değerlerin ne olduğu önemli değilse, sadece birbirinden farklı olması yeterliyse auto() kullanabilirsiniz.
from enum import Enum, auto
class Direction(Enum):
NORTH = auto() # 1
SOUTH = auto() # 2
EAST = auto() # 3
WEST = auto() # 4
for d in Direction:
print(f"{d.name} = {d.value}")
# NORTH = 1
# SOUTH = 2
# EAST = 3
# WEST = 4auto() varsayılan olarak 1'den başlayarak artan integer değerler atar. Flag içinde kullanıldığında ise 2'nin kuvvetlerini kullanır (1, 2, 4, 8...).
from enum import Enum, auto, Flag
class TaskStatus(Enum):
TODO = auto() # 1
IN_PROGRESS = auto() # 2
REVIEW = auto() # 3
DONE = auto() # 4
class Permission(Flag):
READ = auto() # 1
WRITE = auto() # 2
EXECUTE = auto() # 4
ADMIN = auto() # 8
print(TaskStatus.DONE.value) # 4
print(Permission.EXECUTE.value) # 4 (2'nin kuvveti!)auto() Davranışını Özelleştirme
_generate_next_value_ metodunu override ederek auto()'nun ürettiği değerleri kontrol edebilirsiniz:
from enum import Enum, auto
class LowercaseEnum(Enum):
"""auto() üye adını küçük harfle döndürür."""
@staticmethod
def _generate_next_value_(name, start, count, last_values):
return name.lower()
class Color(LowercaseEnum):
RED = auto() # "red"
GREEN = auto() # "green"
BLUE = auto() # "blue"
print(Color.RED.value) # red
print(Color.GREEN.value) # greenBu teknik özellikle API veya veritabanı ile çalışırken çok kullanışlıdır. Üye isimlerini otomatik olarak küçük harfli string'lere dönüştürebilirsiniz.
💡 İpucu:
auto()ileFlagkullandığınızda değerler otomatik olarak 2'nin kuvvetleri olur (1, 2, 4, 8...). Bu sayede bitwise operasyonlar sorunsuz çalışır. Elle değer atarken bu kurala uymayı unutmayın!
9. @unique Decorator
Varsayılan olarak Python, aynı değere sahip birden fazla enum üyesine izin verir. İkinci üye birincinin alias'ı (takma adı) olur:
from enum import Enum
class Shape(Enum):
SQUARE = 2
RECTANGLE = 2 # alias — SQUARE'a eşit
DIAMOND = 3
RHOMBUS = 3 # alias — DIAMOND'a eşit
print(Shape.SQUARE is Shape.RECTANGLE) # True!
print(Shape(2)) # Shape.SQUARE (ilk tanımlanan döner)
print(list(Shape))
# [Shape.SQUARE, Shape.DIAMOND] — alias'lar listede GÖRÜNMEZGördüğünüz gibi RECTANGLE, SQUARE'ın alias'ı haline gelir. Iteration'da alias'lar listelenmez. Bu bazen istenen bir davranış olabilir, ama çoğu zaman bir hata göstergesidir.
Eğer her değerin benzersiz olmasını garantilemek istiyorsanız @unique decorator'ünü kullanın:
from enum import Enum, unique
@unique
class Weekday(Enum):
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 3
THURSDAY = 4
FRIDAY = 5
SATURDAY = 6
SUNDAY = 7
# Bu hata verir:
try:
@unique
class BrokenDay(Enum):
MON = 1
MONDAY = 1 # Aynı değer!
except ValueError as e:
print(e)
# duplicate values found in <enum 'BrokenDay'>: MONDAY -> MON@unique decorator'ü sınıf oluşturulurken kontrol yapar. Aynı değere sahip birden fazla üye bulursa ValueError fırlatır. Bu, büyük enum'larda yanlışlıkla aynı değerin kullanılmasını engeller.
from enum import Enum, unique
@unique
class ErrorCode(Enum):
SUCCESS = 0
INVALID_INPUT = 100
NOT_FOUND = 404
TIMEOUT = 408
SERVER_ERROR = 500
# INTERNAL_ERROR = 500 # Bu satırı açarsanız ValueError alırsınızGenel kural olarak: alias'lara bilinçli olarak ihtiyacınız yoksa her zaman @unique kullanın. Bu, gelecekteki hataları önler.
10. Enum'da Metot Tanımlama
Enum sınıfları normal Python sınıfları gibi metot içerebilir. Bu, enum üyelerine davranış eklemenin güçlü bir yoludur.
from enum import Enum
class Planet(Enum):
MERCURY = (3.303e+23, 2.4397e6)
VENUS = (4.869e+24, 6.0518e6)
EARTH = (5.976e+24, 6.37814e6)
MARS = (6.421e+23, 3.3972e6)
def __init__(self, mass, radius):
self.mass = mass # kg
self.radius = radius # metre
@property
def surface_gravity(self):
G = 6.67300E-11 # m³ kg⁻¹ s⁻²
return G * self.mass / (self.radius ** 2)
def weight_on(self, earth_weight):
"""Dünya'daki ağırlığınızı bu gezegendeki ağırlığa çevirir."""
return earth_weight * self.surface_gravity / Planet.EARTH.surface_gravity
# Kullanım
print(f"Mars yerçekimi: {Planet.MARS.surface_gravity:.2f} m/s²")
weight_on_mars = Planet.MARS.weight_on(75)
print(f"75 kg → Mars'ta {weight_on_mars:.1f} kg")Bu örnekte her gezegenin kütlesi ve yarıçapı tuple olarak saklanır. __init__ metodu bu değerleri ayrıştırır, surface_gravity property'si yerçekimini hesaplar.
Daha basit bir örnek:
from enum import Enum
class Suit(Enum):
"""İskambil kağıdı türleri."""
HEARTS = "♥"
DIAMONDS = "♦"
CLUBS = "♣"
SPADES = "♠"
@property
def is_red(self):
return self in (Suit.HEARTS, Suit.DIAMONDS)
@property
def is_black(self):
return not self.is_red
def describe(self):
color = "Kırmızı" if self.is_red else "Siyah"
return f"{self.name} ({self.value}) — {color}"
for suit in Suit:
print(suit.describe())
# HEARTS (♥) — Kırmızı
# DIAMONDS (♦) — Kırmızı
# CLUBS (♣) — Siyah
# SPADES (♠) — SiyahEnum'a metot ekleyerek hem veriyi hem davranışı tek bir yerde toplamış olursunuz. Bu, nesne yönelimli programlamanın temel ilkelerinden biridir.
11. Enum ve Pattern Matching (match-case)
Python 3.10 ile gelen match-case (yapısal eşleme), enum'larla mükemmel çalışır. if-elif zincirlerine kıyasla çok daha temiz ve okunabilir kod yazmanızı sağlar.
from enum import Enum
class Command(Enum):
QUIT = "quit"
SAVE = "save"
LOAD = "load"
NEW = "new"
def handle_command(cmd: Command):
match cmd:
case Command.QUIT:
print("Çıkılıyor...")
case Command.SAVE:
print("Kaydediliyor...")
case Command.LOAD:
print("Yükleniyor...")
case Command.NEW:
print("Yeni dosya oluşturuluyor...")
case _:
print("Bilinmeyen komut!")
handle_command(Command.SAVE) # Kaydediliyor...match-case ile enum kullandığınızda her case dalında enum üyesini doğrudan yazarsınız. Wildcard (_) ile bilinmeyen durumları yakalarsınız.
from enum import Enum
class Season(Enum):
SPRING = 1
SUMMER = 2
AUTUMN = 3
WINTER = 4
def season_advice(season: Season) -> str:
match season:
case Season.SPRING:
return "🌸 Alerjinize dikkat edin!"
case Season.SUMMER:
return "☀️ Bol su için, güneş kremi sürün."
case Season.AUTUMN:
return "🍂 Yağmurluk almayı unutmayın."
case Season.WINTER:
return "❄️ Kalın giyinin, yollar kaygan olabilir."
for s in Season:
print(f"{s.name}: {season_advice(s)}")Pattern matching'in güzel yanlarından biri, IDE ve linter'ların eksik case'leri yakalayabilmesidir. Enum'a yeni bir üye ekleyip match-case'e eklemeyi unutursanız, statik analiz araçları sizi uyarabilir.
12. Gerçek Dünya Örneği: Sipariş Durumu Yönetimi
Şimdi tüm öğrendiklerimizi bir araya getirelim. E-ticaret uygulamalarında en yaygın kullanım alanlarından biri: sipariş durumu yönetimi.
Bir sipariş şu aşamalardan geçer: PENDING → PAID → SHIPPED → DELIVERED. Her geçiş belirli kurallara tabidir — örneğin "PENDING" durumundaki bir sipariş doğrudan "DELIVERED" olamaz.
from enum import Enum, unique, auto
from datetime import datetime
@unique
class OrderStatus(Enum):
PENDING = "pending"
PAID = "paid"
SHIPPED = "shipped"
DELIVERED = "delivered"
CANCELLED = "cancelled"
REFUNDED = "refunded"
@property
def label_tr(self):
"""Türkçe etiket döndürür."""
labels = {
OrderStatus.PENDING: "Beklemede",
OrderStatus.PAID: "Ödendi",
OrderStatus.SHIPPED: "Kargoda",
OrderStatus.DELIVERED: "Teslim Edildi",
OrderStatus.CANCELLED: "İptal Edildi",
OrderStatus.REFUNDED: "İade Edildi",
}
return labels[self]
@property
def allowed_transitions(self):
"""Bu durumdan geçilebilecek durumları döndürür."""
transitions = {
OrderStatus.PENDING: {OrderStatus.PAID, OrderStatus.CANCELLED},
OrderStatus.PAID: {OrderStatus.SHIPPED, OrderStatus.REFUNDED},
OrderStatus.SHIPPED: {OrderStatus.DELIVERED},
OrderStatus.DELIVERED: {OrderStatus.REFUNDED},
OrderStatus.CANCELLED: set(),
OrderStatus.REFUNDED: set(),
}
return transitions[self]
def can_transition_to(self, new_status):
"""Bu durumdan hedef duruma geçiş mümkün mü?"""
return new_status in self.allowed_transitionsŞimdi bu enum'u kullanan bir Order sınıfı yazalım:
class Order:
def __init__(self, order_id: str, customer: str):
self.order_id = order_id
self.customer = customer
self.status = OrderStatus.PENDING
self.history = [(OrderStatus.PENDING, datetime.now())]
def transition_to(self, new_status: OrderStatus):
"""Sipariş durumunu güvenli şekilde değiştirir."""
if not isinstance(new_status, OrderStatus):
raise TypeError(f"OrderStatus bekleniyor, {type(new_status)} geldi")
if not self.status.can_transition_to(new_status):
raise ValueError(
f"Geçersiz geçiş: {self.status.label_tr} → {new_status.label_tr}. "
f"İzin verilenler: {[s.label_tr for s in self.status.allowed_transitions]}"
)
old_status = self.status
self.status = new_status
self.history.append((new_status, datetime.now()))
print(f"[{self.order_id}] {old_status.label_tr} → {new_status.label_tr}")
def __repr__(self):
return f"Order({self.order_id}, status={self.status.label_tr})"Ve kullanım:
# Sipariş oluştur
order = Order("ORD-2024-001", "Ahmet")
print(order) # Order(ORD-2024-001, status=Beklemede)
# Normal akış: PENDING → PAID → SHIPPED → DELIVERED
order.transition_to(OrderStatus.PAID) # ✓ Beklemede → Ödendi
order.transition_to(OrderStatus.SHIPPED) # ✓ Ödendi → Kargoda
order.transition_to(OrderStatus.DELIVERED) # ✓ Kargoda → Teslim Edildi
# Geçersiz geçiş deneyelim
try:
order2 = Order("ORD-2024-002", "Mehmet")
order2.transition_to(OrderStatus.DELIVERED) # PENDING → DELIVERED?
except ValueError as e:
print(f"Hata: {e}")
# Hata: Geçersiz geçiş: Beklemede → Teslim Edildi.
# İzin verilenler: ['Ödendi', 'İptal Edildi']Bu örnekte enum'un gücünü görüyorsunuz:
Tip güvenliği: Sadece
OrderStatusdeğerleri kabul edilir, typo imkansız.Geçiş kuralları: Her durumdan hangi durumlara geçilebileceği enum içinde tanımlı.
Okunabilirlik:
OrderStatus.SHIPPEDifadesistatus = 2'den çok daha anlaşılır.Bakım kolaylığı: Yeni bir durum eklemek istediğinizde tek bir yeri güncellersiniz.
Bu yapıyı section 11'deki match-case ile birleştirerek notify_customer gibi fonksiyonlar da yazabilirsiniz — her durum için farklı bildirim mesajları üreten temiz bir pattern matching örneği.
13. Enum vs String Sabitleri vs Dict — Ne Zaman Hangisi?
Bu soru çok sık sorulur. Üç yaklaşımı karşılaştıralım:
String Sabitleri
# Basit string sabitleri
STATUS_ACTIVE = "active"
STATUS_INACTIVE = "inactive"
STATUS_DELETED = "deleted"
# Avantajlar: Basit, import gerektirmez
# Dezavantajlar: Typo riski, gruplandırma yok,
# IDE desteği sınırlı, iteration yok
user_status = "actve" # Typo — hata vermez!String sabitleri en basit yaklaşımdır. Küçük script'lerde veya hızlı prototiplerde işe yarar. Ancak büyük projelerde typo riski ve gruplandırma eksikliği ciddi sorunlara yol açar.
Dictionary Sabitleri
# Dictionary ile sabitler
STATUSES = {
"active": "Aktif",
"inactive": "Pasif",
"deleted": "Silinmiş",
}
# Avantajlar: Key-value eşleştirme, iteration
# Dezavantajlar: Runtime'da değiştirilebilir,
# tip güvenliği yok, IDE autocomplete zayıf
STATUSES["hacked"] = "Hacklenmiş" # Kolayca bozulabilirDictionary'ler key-value eşleştirmesi gerektiğinde kullanışlıdır. Ancak mutable (değiştirilebilir) oldukları için sabit değer garantisi vermezler. Yanlışlıkla bir key eklenebilir veya silinebilir.
Enum
from enum import Enum, unique
@unique
class Status(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
DELETED = "deleted"
@property
def label(self):
labels = {
Status.ACTIVE: "Aktif",
Status.INACTIVE: "Pasif",
Status.DELETED: "Silinmiş",
}
return labels[self]
# Avantajlar: Tip güvenliği, immutable, IDE desteği,
# iteration, metot desteği, pattern matching
# Dezavantajlar: Biraz daha verbose
Status.ACTIVE.label # "Aktif"
Status("active") # Status.ACTIVEKarar Tablosu
| Kriter | String Sabitleri | Dict | Enum |
|---|---|---|---|
| Tip güvenliği | ❌ | ❌ | ✅ |
| Typo koruması | ❌ | ❌ | ✅ |
| IDE autocomplete | ⚠️ | ❌ | ✅ |
| Immutable | ⚠️ | ❌ | ✅ |
| Iteration | ❌ | ✅ | ✅ |
| Metot desteği | ❌ | ❌ | ✅ |
| Pattern matching | ⚠️ | ❌ | ✅ |
| Basitlik | ✅ | ✅ | ⚠️ |
| Performans | ✅ | ✅ | ✅ |
Ne Zaman Hangisini Kullanmalı?
String sabitleri: Küçük script'ler, 2-3 değer, hızlı prototip.
Dictionary: Key-value eşleştirmesi birincil ihtiyaçsa, veriler runtime'da değişebiliyorsa.
Enum: Sonlu/sabit değer kümesi, tip güvenliği, okunabilirlik, birden fazla yerde kullanım, pattern matching.
💡 İpucu: Bir sabit grubunu 2'den fazla yerde kullanıyorsanız enum'a geçmeyi düşünün. Başlangıçta biraz daha fazla kod yazarsınız ama uzun vadede bakım maliyetiniz düşer, hata oranınız azalır.
Özet
Enum, sonlu ve sabit değerler kümesini temsil eder. Magic number'ları ortadan kaldırarak kodunuzu okunabilir ve güvenli hale getirir.
IntEnum ve StrEnum, sırasıyla
intvestrile uyumlu enum'lardır. Primitive tiplerle doğrudan karşılaştırma ve işlem yapmanızı sağlar ancak tip güvenliğinden ödün verirsiniz.Flag ve IntFlag, bitwise
|operatörüyle birleştirilebilen enum'lardır. Dosya izinleri, kullanıcı yetkileri gibi çoklu durum senaryolarında kullanılır.auto() fonksiyonu üyelere otomatik değer atar —
Enum'da sıralı integer,Flag'da 2'nin kuvvetleri._generate_next_value_ile özelleştirilebilir.@unique decorator'ü aynı değere sahip birden fazla üyeyi yasaklar. Büyük enum'larda yanlışlıkla alias oluşturmayı önlemek için her zaman kullanmanız önerilir.
Enum'a metot ekleyerek hem veriyi hem davranışı tek bir yerde toplayabilirsiniz. Pattern matching (
match-case) ile birlikte kullanıldığında son derece temiz ve bakımı kolay kod elde edersiniz.
*Bu ders, Python'un enum modülünün temellerinden ileri tekniklerine kadar geniş bir yelpazede bilgi sunmaktadır. Tüm örnekleri kendi bilgisayarınızda deneyerek öğrenmenizi pekiştirmeniz önerilir. Python 3.11+ kullanıyorsanız StrEnum'u, 3.10+ kullanıyorsanız match-case'i mutlaka deneyin.*
AI Asistan
Sorularını yanıtlamaya hazır