İçeriğe geç

bool ve None: Truthiness'in Felsefesi ve Sentinel Değer Sanatı

Python'da `True` aslında `int`'in alt sınıfı (`True + 1 == 2`!). Her tip 'doğru/yanlış' bağlamında değerlendirilebilir — buna 'truthiness' denir. None ise 'değer yok' anlamına gelen tek-elemanlı bir sentinel. Bu derste: bool gerçek doğası, falsy değerler tablosu, `is None` vs `== None`, Optional type hint, default arg sentinel pattern, ve günlük kodda en sık karşına çıkan 'küçük' detayların derinliği.

Şükrü Yusuf KAYA
20 dakikalık okuma
Başlangıç
bool ve None: Truthiness'in Felsefesi ve Sentinel Değer Sanatı
🤯 Bu ders küçük gibi görünür ama her gün kullanırsın
True/False ve None — her Python kodunda. Ama detaylar bilinmediğinde sürpriz: True + 1 == 2, [] truthy mi falsy mi (falsy), None is None vs == None — birinin kullanılmaması var. Bu derste 'her gün karşına çıkan' kavramların hayatın derinliğini öğreneceksin.

bool — int'in alt sınıfı (gerçekten!)#

Python'da
bool
özel bir tip ama altta
int
. Yani:
>>> isinstance(True, int) True >>> type(True) <class 'bool'> >>> bool.__bases__ (<class 'int'>,) # bool, int'ten miras alıyor >>> True + 1 2 >>> True * 5 5 >>> False * 100 0 >>> sum([True, False, True, True]) 3 # True'lar 1, False'lar 0
Bu özellik kasıtlı. Eski Python'larda (2.0 öncesi) bool yoktu —
0
ve
1
kullanılıyordu. Sonra Python 2.3'te
bool
eklendi ama geriye uyumluluk için
int
'in alt sınıfı yapıldı.
Pratik faydası:
sum()
ile bool listesindeki True'ları sayabilirsin.
data = [10, 20, 30, 40, 50] # Kaç elemanı 25'ten büyük? count = sum(1 for x in data if x > 25) # geleneksel count = sum(x > 25 for x in data) # daha kısa, bool aritmetiği print(count) # 3
Bu pratik özellik veri analizinde sıkça kullanılır —
pandas.DataFrame.sum()
boolean kolonlarda da çalışıyor.
🚨 Bool olduğunu düşündüğün şey aslında int gibi davranır. Kötü patterns:
# 🚫 (çalışıyor ama yanıltıcı) score = True + 5 # 6 done = False / 1 # 0.0 # ✅ score = 5 + (1 if condition else 0)

Truthiness — her şey "doğru veya yanlış" olabilir#

Python'da
if x:
yazdığında
x
herhangi bir tip olabilir. Python ona "doğru mu yanlış mı?" diye soruyor. Bu kavrama truthiness (doğruluk) deniyor.

Falsy (yanlış değerlendirilen) değerler#

Sadece şu değerler falsy:
# Tüm falsy değerler: False # bool None # NoneType 0 # int 0.0 # float 0j # complex "" # boş string [] # boş list () # boş tuple {} # boş dict set() # boş set range(0) # boş range b"" # boş bytes bytearray() # boş bytearray
Diğer her şey truthy. Bu liste önemli; ezberle:
bool(False) # False bool(None) # False bool(0) # False bool(0.0) # False bool("") # False bool([]) # False bool({}) # False bool(True) # True bool(1) # True bool(-1) # True (sıfır olmayan her int truthy) bool(0.0001) # True bool("0") # True! (boş olmayan string truthy) bool("False") # True! (içerik "False" yazısı, boş değil) bool([0]) # True! (içinde 0 var ama liste boş değil) bool([False]) # True! bool({0: 0}) # True! (dict boş değil)
🎯 Yıldızlı tuzaklar:
  • "0"
    truthy çünkü boş değil!
  • "False"
    truthy çünkü içeriği "F-a-l-s-e" karakterleri.
  • [0]
    truthy çünkü liste boş değil (içeriğine bakmaz, varlığa bakar).
Bunlar JavaScript ile farklı (
"0"
JS'de truthy,
"false"
da truthy ama JS'de
!"" === true
).

Truthiness'i Pythonic kullanmak#

Truthiness Python'a "kısa, anlamlı" if statement'ları yazmana imkan veriyor:
# Liste boş mu kontrol etme items = [] # 🚫 Java aksanı if len(items) == 0: print("Boş") # 🚫 Daha da kötü if items == []: print("Boş") # ✅ Pythonic if not items: print("Boş") # String boş mu name = "" # 🚫 if len(name) == 0: do_something() # ✅ if not name: do_something() # Default değer (or kısa-circuit) display_name = user.name or "Anonim" # name boşsa "Anonim" # Listenin ilk truthy elemanı first_truthy = next((x for x in items if x), None)

Ama dikkat — bazı durumlarda explicit kontrol şart#

def process(value): # 🚫 Hatalı: 0, "", [], None hepsi falsy ama anlamları farklı if not value: return "Eksik veri" # value=0 da bunu tetikler! # ✅ Doğru: None'ı spesifik kontrol et if value is None: return "Eksik veri" if value == 0: return "Sıfır değer" if not value: # boş list/string vs return "Boş" return f"Değer: {value}" # Test print(process(None)) # 'Eksik veri' print(process(0)) # 'Sıfır değer' print(process("")) # 'Boş' print(process([])) # 'Boş' print(process(42)) # 'Değer: 42'
Pratik kural: Sayısal kontekstlerde (0 anlamlı bir değer olabilir) explicit
is None
veya
== 0
kontrol yap. Container'larda (list/dict/string)
if not items:
truthy-check kullan.

and
ve
or
— short-circuit evaluation#

Python'da
and
/
or
C/Java'dakinden biraz farklı. Boolean değil, son değerlendirilen değeri dönüyor.
# and: ilk falsy değeri (veya son truthy değeri) döner print(5 and 10) # 10 (ikisi de truthy, son truthy) print(0 and 10) # 0 (ilk falsy — 10'a hiç bakmadı) print("" and "hello") # "" (boş string falsy) print([1] and [2]) # [2] # or: ilk truthy değeri (veya son falsy değeri) döner print(5 or 10) # 5 (ilk truthy) print(0 or 10) # 10 (0 falsy, 10'a baktı) print(None or "default") # "default" print("" or [] or "x") # "x"

Short-circuit avantajı#

# Default değer pattern display_name = user.nickname or user.username or "Misafir" # Sırayla: nickname truthy mi? Değilse username? Değilse "Misafir". # Lazy attribute access data = response and response.json() # response None ise, .json() çağırma; AttributeError'dan korur. # Conditional execution data and process(data) # data truthy ise process'i çağır
🚨
or
ile default tuzak
: Eğer falsy ama anlamlı bir değer varsa (örn. 0):
# Tuzak def get_count(items=None): count = items.count or 100 # items.count = 0 ise default 100 — istemediğin return count # Doğrusu def get_count(items=None): if items is None: count = 100 else: count = items.count return count # Veya ternary count = items.count if items is not None else 100
or
"None ya da default" ihtiyacı için
if x is None
ile değiştir;
or
boolean değil sayısal/koleksiyon değerlerle güvenilir değil.

Custom class'larda truthiness —
__bool__
ve
__len__
#

Kendi sınıfını da truthy/falsy yapabilirsin:
class ShoppingCart: def __init__(self): self.items = [] def add(self, item): self.items.append(item) def __bool__(self): """if cart: ... için.""" return bool(self.items) cart = ShoppingCart() print(bool(cart)) # False (boş items) if cart: print("Sepet dolu") else: print("Sepet boş") # bunu yazar cart.add("Elma") print(bool(cart)) # True
Eğer
__bool__
yoksa Python
__len__
kullanır:
class Container: def __init__(self, items): self.items = items def __len__(self): return len(self.items) c1 = Container([]) c2 = Container([1, 2, 3]) print(bool(c1)) # False (len 0) print(bool(c2)) # True (len > 0)
Hiçbir method tanımlanmamışsa: nesne her zaman truthy (None hariç).
class Empty: pass print(bool(Empty())) # True (default) print(bool(None)) # False (özel)
Bu pattern Pythonic kod yazımının ana yollarından biri —
if cart:
veya
if user:
gibi doğal okuyan koşullar.

None — Python'un null'u#

None
, "değer yok" anlamında özel bir nesne. Tek-elemanlı bir tip:
>>> type(None) <class 'NoneType'> # None her zaman aynı nesne (singleton) >>> a = None >>> b = None >>> a is b True # Hiç değiştirilemez (immutable) >>> None.x = 5 AttributeError: 'NoneType' object has no attribute 'x' # Aritmetik yok >>> None + 1 TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

Ne için kullanılıyor?#

  1. Fonksiyon return değeri olmazsa default:
def greet(name): print(f"Merhaba {name}") # return yok! result = greet("Ali") print(result) # None (otomatik)
  1. Default argüman sentinel:
def search(query, limit=None): if limit is None: limit = 100 # ...
  1. Optional değer (var ya da yok):
def find_user(user_id): if user_id in database: return database[user_id] return None # Yok
  1. Mutable default tuzağına çözüm (Modül 2/Ders 1 hatırla):
def collect(items=None): if items is None: items = [] # ...
  1. Sentinel değer:
DELETED = None # işaretlemek için records = {1: "Ali", 2: DELETED, 3: "Ayşe"}

is None
vs
== None
— kritik fark#

Bu, Python'un en sık karıştırılan kurallarından biri. Cevap: her zaman
is None
.
# 🚫 if x == None: ... # ✅ if x is None: ...
Neden?
  1. Tutarlılık:
    None
    tek bir nesne (singleton). Identity check (
    is
    ) bu nesneye bağlı; equality (
    ==
    ) custom class tarafından override edilebilir.
class Weird: def __eq__(self, other): return True # her şeyle eşit gibi davranıyor! w = Weird() print(w == None) # True (yanıltıcı!) print(w is None) # False (gerçek)
  1. Performans:
    is
    daha hızlı. id karşılaştırması, method çağrısı yok.
  2. PEP 8 önerisi: Resmi.
  3. Linter zorlar: Ruff, flake8, pylint hepsi
    == None
    'a uyarı atıyor.
# Linter çıktısı if x == None: # E711 comparison to None should be 'if cond is None:'

Aynı kural: True ve False için de mı?#

PEP 8'e göre: hayır.
True
ve
False
için
== True
veya
is True
ikisi de OK ama explicit karşılaştırma çoğu zaman gereksiz.
# 🚫 Gereksiz if is_active == True: ... # ✅ Direkt if is_active: ... # 🚫 if condition is True: ... # ✅ if condition: ...
Bool için truthiness kullan, == ile karşılaştırma sürpriz davranışlar üretebilir (örneğin
1 == True
True döner ama
1 is True
False).

Type hint:
Optional
ve
X | None
#

Python type hints'te "değer veya None olabilir" demek için Optional kullan:
from typing import Optional def find_user(user_id: int) -> Optional[str]: """Kullanıcı bulunursa adı, yoksa None döner.""" if user_id in database: return database[user_id] return None
Optional[X]
=
Union[X, None]
=
X | None
(Python 3.10+).
Modern Python (3.10+) için pipe syntax tercih ediliyor:
# Eski (3.9-) def find_user(user_id: int) -> Optional[str]: ... # Modern (3.10+) def find_user(user_id: int) -> str | None: ...
İkisi aynı şey. PEP 604.

Optional
argüman da olabilir#

def search(query: str, limit: int | None = None) -> list[str]: if limit is None: limit = 100 # ...
mypy/pyright bu signature'ı görünce, fonksiyon kullanan kodda None handling kontrolü yapıyor.
result = find_user(1) print(result.upper()) # mypy uyarır: result might be None! # Doğrusu — None check result = find_user(1) if result is not None: print(result.upper())
Type system seni güvenli kod yazmaya yönlendiriyor.

Sentinel pattern — None'ın "anlamlı yokluk"u#

Bazen
None
"anlamlı bir değer olabilir; eksik mi yoksa explicit None mu?" karışıklığı yaratır:
def fetch(default=None): """default belirtilmemişse 100, None gelirse None bırak.""" if default is None: return 100 # ama belki kullanıcı bilerek None demek istedi! return default
Çözüm: özel sentinel kullan.
# Pattern 1: object() ile unique sentinel _MISSING = object() def fetch(default=_MISSING): if default is _MISSING: return 100 # parametre verilmedi return default # default = None bile olsa kullan # Test fetch() # 100 (verilmedi) fetch(None) # None (explicit None istendi) fetch(50) # 50 # Pattern 2: enum sentinel (Python 3.10+'ta typing.NoReturn benzeri) import enum class _Sentinel(enum.Enum): MISSING = "MISSING" MISSING = _Sentinel.MISSING def fetch(default=MISSING): if default is MISSING: return 100 return default
object()
her çağrıda unique nesne dönüyor — kimse aynı sentinel'a ulaşamaz, identity check güvenilir.
Bu pattern stdlib'de de kullanılıyor:
# inspect.Parameter.empty bir sentinel import inspect sig = inspect.signature(lambda x: x) param = list(sig.parameters.values())[0] print(param.default is inspect.Parameter.empty) # True
Kütüphane geliştirirken bu pattern hayat kurtarıyor —
None
'ın iki anlamı (parametre yok vs explicit None) ayrılıyor.

Pratik patterns — gerçek kodda#

Pattern 1: Late default initialization#

def add_log(message, timestamp=None): if timestamp is None: timestamp = datetime.now() log.append((timestamp, message))
Default
datetime.now()
olamaz — fonksiyon tanımlandığı an bir kez evaluate olur, her çağrıda aynı zaman!

Pattern 2: Lazy property#

class User: def __init__(self, user_id): self.user_id = user_id self._profile = None # cache @property def profile(self): if self._profile is None: self._profile = fetch_profile(self.user_id) # pahalı return self._profile
None
"henüz fetch edilmedi" sinyali. İlk erişimde fetch, sonra cache.

Pattern 3: Optional zinciri (chaining)#

# Java'da: user?.profile?.address?.city # Python equivalent: user = get_user(1) city = user.profile.address.city if (user and user.profile and user.profile.address) else None # Daha temiz pattern (3.8+ walrus + or): city = (user and user.profile and user.profile.address and user.profile.address.city) or "Unknown" # Veya try-except: try: city = user.profile.address.city except AttributeError: city = None # Veya getattr ile (en temiz): def safe_get(obj, *attrs): for attr in attrs: if obj is None: return None obj = getattr(obj, attr, None) return obj city = safe_get(user, 'profile', 'address', 'city')

Pattern 4:
None
veya
raise
— hangi ne zaman?#

# Hata durumu için: # Yaklaşım 1: None dön (Pythonic, çağıran kontrol etsin) def find_user(user_id): return database.get(user_id) # None if not found user = find_user(123) if user is None: print("Bulunamadı") # Yaklaşım 2: Exception fırlat (Pythonic when 'eksik' anormal) def get_user(user_id): if user_id not in database: raise KeyError(f"User {user_id} not found") return database[user_id] try: user = get_user(123) except KeyError: print("Bulunamadı") # Yaklaşım 3: Default dön def get_user_or_default(user_id, default=None): return database.get(user_id, default)
Karar:
  • "Eksik" beklenebilir →
    None
    dön (
    dict.get()
    gibi)
  • "Eksik" hata durumu → Exception fırlat (
    dict[key]
    gibi)
  • Çağıran her durumda devam edecek → default ile dön
Modern type-safe yaklaşım:
Optional[User]
veya
Result[User, NotFound]
(Rust tarzı).

Yaygın bool/None tuzakları#

1.
if x:
ile sayısal veri#

def process(count): if count: # 🚫 do_work(count) # count=0 truthy değil! Bu fonksiyon 0 ile çağrıldığında do_work atlanır. # ✅ def process(count): if count > 0: do_work(count) # Veya: def process(count): if count is not None and count > 0: do_work(count)

2.
return
unutma#

def find(name, items): for item in items: if item.name == name: return item # return None EKSIK ama Python otomatik None dönüyor # Tip belli olsun diye explicit: def find(name, items): for item in items: if item.name == name: return item return None # explicit
Linter
PLR1711
kuralı bunu uyarır. Modern stil: explicit
return None
.

3.
None
karşılaştırması#

# 🚫 if x == None: ... # ✅ if x is None: ...

4. Bool ile string formatla#

status = True print(f"Status: {status}") # "Status: True" print(f"Status: {int(status)}") # "Status: 1" # JSON'da import json json.dumps({"active": True}) # '{"active": true}' (lowercase!)
JSON
true
lowercase. Python
True
capital. Encoder otomatik çeviriyor.

5. Empty collection truthiness#

results = search(query) # 🚫 Belirsiz if results: process(results) # Anlam: results None değilse VE liste boş değilse process et # Bu doğru mu? Bağlama bakıyorsun. Tehlikeli yer. # ✅ Açık if results is not None: if results: # boş değil process(results) else: print("Sonuç yok") # Veya: if results: process(results) elif results is None: print("Hata: arama yapılamadı") else: print("Sonuç bulunamadı")

6. Bool aritmetik istemeyen yerde#

# 🚫 Niyeti belli değil total = price + bonus * (rating > 4) # rating > 4 → True/False → 0/1 — çoğu okuyucu bilmiyor # ✅ bonus_amount = bonus if rating > 4 else 0 total = price + bonus_amount

Bu derste neler kazandın?#

bool int'in alt sınıfı (True + 1 == 2!) — geriye uyumluluk hediyesi.
Truthiness — her tip "doğru/yanlış" değerlendirilebilir.
Falsy değerler tablosu: False, None, 0, 0.0, 0j, "", [], (), {}, set(), b"".
if not items:
Pythonic boş kontrolü.
and/or short-circuit — boolean değil son değerlendirilen değer döner.
__bool__
ve
__len__
ile custom truthiness.
None — singleton, "değer yok" sentinel.
is None
vs
== None
— neden hep
is
.
Optional type hint
Optional[X]
ve modern
X | None
.
Sentinel pattern
object()
ile unique sentinel ile None'ın iki anlamı arasını ayırma.
5 pratik pattern: late default init, lazy property, Optional zinciri, None vs raise, default ile dön.
6 yaygın tuzak — sayısal truthiness, return unutma, == None, JSON casing, empty collection, bool aritmetik.
Sıradaki ders: Aritmetik operatörler. Görünüşte basit
+ - * /
ama Python'da operator overloading var —
Vector(1,2) + Vector(3,4)
nasıl çalışır?
__add__
,
__radd__
,
__iadd__
,
__neg__
magic methodları, Vector ve Money sınıflarıyla pratik örnekler.

Sık Sorulan Sorular

Tamamen tasarım kararı. Guido van Rossum ABC dilinden ilham aldı (None ABC'den geliyor). 'null' diğer dillerde tip-belirsizlikten doğan kafa karışıklığı yaratıyor (Java'da NullPointerException, JS'de undefined vs null karmaşası). Python tek bir 'değer yok' sentinel'ı: None. Tek tip (NoneType), tek nesne (singleton), tutarlı davranış.

Yorumlar & Soru-Cevap

(0)
Yorum yazmak için giriş yap.
Yorumlar yükleniyor...

İlgili İçerikler