İçeriğe geç

Operator Precedence ve Assosiyatiflik: Parantezsiz Doğru Kod Yazma Sanatı

'a or b and c' nasıl evaluate edilir? '5 + 3 * 2' neden 11 değil 16? '~5 << 2' nedir? Python'un 18 seviyeli precedence tablosu ve sol/sağ assosiyatiflik kuralları. Bu derste: tam precedence tablosu, klasik tuzaklar, IDE warning'lerinin neden olduğu davranışları, ve 'parantez ne zaman gerek' net kararı.

Şükrü Yusuf KAYA
16 dakikalık okuma
Orta
Operator Precedence ve Assosiyatiflik: Parantezsiz Doğru Kod Yazma Sanatı
🎓 Bu konu sınav için değil — bug için
Hiçbir programcı operator precedence tablosunu ezbere bilmez. Ama tabloyu hiç tanımayan birinin kodunda subtle bug'lar oluyor:
x & 1 == 0
yazınca
x & (1 == 0)
çalışıyor — beklenmedik. Bu derste bilmen gereken kritik kurallar ve ne zaman parantez kullanmak şart.

Tam Precedence Tablosu (en yüksekten düşüğe)#

Python'un resmi tablosu — ezberlemen gerekmiyor, ama tanıdık olman lazım:
SeviyeOperatörİsimAsosiyatiflik
1 (en yüksek)
(...)
,
[...]
,
{...}
Parantez, indeks, koleksiyon
2
x[i]
,
x[i:j]
,
x(arg)
,
x.attr
Subscription, slicing, call, attributesol
3
await x
Await
4
**
Üssağ
5
+x
,
-x
,
~x
Unarysağ
6
*
,
@
,
/
,
//
,
%
Çarpma, matrix mul, bölme, floor, modsol
7
+
,
-
Toplama, çıkarmasol
8
<<
,
>>
Shiftsol
9
&
Bitwise ANDsol
10
^
Bitwise XORsol
11``Bitwise OR
12
<
,
<=
,
>
,
>=
,
==
,
!=
,
is
,
in
Karşılaştırma, identity, membership— (chain)
13
not x
Mantıksal NOTsağ
14
and
Mantıksal ANDsol
15
or
Mantıksal ORsol
16
if-else
Conditional expression (ternary)sağ
17
lambda
Lambda
18 (en düşük)
:=
Walrus
🎯 Anahtar gözlemler:
  • Parantez en üstte — her zaman önce evaluate edilir.
  • **
    sağ assosiyatif
    :
    2**3**2 == 2**(3**2) == 2**9 == 512
    (NOT
    (2**3)**2 == 64
    ).
  • Aritmetik > shift > bitwise > comparison > logical.
  • and
    >
    or
    .
  • Comparison'lar chain edilebilir (Modül 2/Ders 10).

Klasik kurallar — okul matematiği#

Aritmetik kısmı zaten biliyorsun ama Python'a uyarlayalım:
# Çarpma > Toplama print(5 + 3 * 2) # 11 (NOT 16) print((5 + 3) * 2) # 16 # Üs > unary print(-2 ** 2) # -4 (NOT 4!) # Çünkü: -(2 ** 2) = -4 # Eğer (-2)**2 istiyorsan parantez gerek print((-2) ** 2) # 4 # ** sağ assosiyatif print(2 ** 3 ** 2) # 512 = 2**(3**2) = 2**9 print((2 ** 3) ** 2) # 64 # Bölme türleri aynı seviye, sol assosiyatif print(20 / 4 / 2) # 2.5 = (20 / 4) / 2 # Modulus aynı seviye print(10 % 6 % 4) # 0 = (10 % 6) % 4 = 4 % 4 = 0
-2 ** 2 == -4
çoğu öğrencinin tökezlediği yer. Sebep:
**
daha yüksek precedence, unary minus daha düşük. Yani
- (2**2)
olarak parse ediliyor.
Pratik: hız değil okunabilirlik için belirsiz olduğunda parantez kullan.

Aldatıcı durumlar — şüphe duyduğunda parantez#

1. Bit AND ile karşılaştırma#

# 🚫 Beklenmedik x = 5 if x & 1 == 1: print("Tek sayı") # Aslında: if x & (1 == 1): # = if x & True: # = if x & 1: → True (x bit 0 = 1) # Doğru çalışıyor gibi görünüyor — ama farklı sayılarda farklı sonuç y = 4 if y & 1 == 1: print("Tek sayı") # 0 (False) # Aslında: y & (1 == 1) = y & 1 = 0 — doğru # Bu örnekte tesadüfen doğru çalıştı — ama mantık yanlış # 'x & 1 == 1' diye düşünüyorsun, '(x & 1) == 1' demek # Python ise x & (1 == 1) yapıyor — '==' precedence > '&' # ✅ Açık if (x & 1) == 1: print("Tek")
==
precedence'i
&
ve
|
'den yüksek — bu çok yaygın hata. Bit op + comparison kombinasyonunda mutlaka parantez.

2. Walrus + comparison#

# 🚫 if (n := len(items) > 10): print(f"n = {n}") # Beklenen: n = len(items) # Aslında: n = (len(items) > 10) → True/False # n bool! # ✅ if (n := len(items)) > 10: print(f"n = {n}") # Burada n int olarak atanıyor, sonra > 10 karşılaştırılıyor
Walrus precedence'ı en düşük — parantez ile sınırla.

3.
not
ile
in
#

# 🚫 if not x in lst: ... # Çalışıyor ama anlam: (not x) in lst — yanlış mantıksal yapı # ✅ if x not in lst: ... # 'not in' tek operator (compound) # Gerçi bu örnekte ikisi de aynı sonuç çoğu zaman ama # semantic farklı — okuyana 'not in' daha açık

4. Conditional expression iç içe#

# 🚫 Okunmaz result = a if cond1 else b if cond2 else c # Aslında nasıl evaluate ediliyor: # result = a if cond1 else (b if cond2 else c) # Yani: cond1 ise a, değilse (cond2 ise b, değilse c) # ✅ Daha açık if cond1: result = a elif cond2: result = b else: result = c # Veya match match (cond1, cond2): case (True, _): result = a case (False, True): result = b case _: result = c
Nested ternary 2'den fazlaysa elif'e dönüş.

5.
and
/
or
öncelik sırası#

# 🚫 if x or y and z: ... # Aslında: if x or (y and z): # and > or olduğu için y and z önce # Eğer (x or y) and z istiyordun: if (x or y) and z: ...
and
precedence'ı
or
'dan yüksek. Mantıksal "or" ve "and" karıştığında parantez şart.

6.
=
(atama) ve
==
karışmaz#

Python'da
if x = 5
syntax error verir — assignment ifade değil. C'de bu hata yok (bug kaynağı). Python bunu önlüyor. Walrus
:=
ifade ama bu da sınırlı yerlerde geçerli.

Assosiyatiflik — sol mu sağ mı?#

İki aynı precedence'lı operatör arka arkaya geldiğinde hangi yön evaluate ediliyor:
# Sol assosiyatif (most operators) 20 - 5 - 3 # = (20 - 5) - 3 = 15 - 3 = 12 (NOT 20 - (5-3) = 18) 10 / 2 / 5 # = (10/2) / 5 = 5 / 5 = 1.0 # Sağ assosiyatif — özel: ** ve atama 2 ** 3 ** 2 # = 2 ** (3 ** 2) = 2 ** 9 = 512 a = b = c = 5 # = a = (b = (c = 5)) — sağ assosiyatif # Tüm değişkenler 5
**
sağ assosiyatif çünkü matematiksel notasyonda öyle:
2^3^2 = 2^(3^2)
. Diğer Python operatörleri sol.

Comparison chaining — özel davranış#

# 0 < x < 10 — chain (Modül 2/Ders 10) # Bu sol veya sağ değil; özel chaining kuralı a < b < c # = (a < b) and (b < c) — short-circuit a == b == c # = (a == b) and (b == c)
Comparison'lar normal binary op'lar gibi değil; her ikisi short-circuit'le and ile bağlanıyor.

Pratik kural: Parantez ne zaman?#

Stil rehberlerine baktığımızda iki yaklaşım var:

Minimal parantez (matematik tarzı)#

result = a + b * c # tabii ki çarpma önce mask = ~bits & 0xFF # bit op'lar normal sıra
Avantaj: kısa, matematiksel olarak doğal. Dezavantaj: takım arkadaşın precedence'ı senin kadar bilmeyebilir.

Defensif parantez (savunmacı)#

result = a + (b * c) # gereksiz ama net mask = (~bits) & 0xFF # gereksiz ama net
Avantaj: şüphe yok. Dezavantaj: dağınık kod.
Pratik orta yol — şu durumlarda mutlaka parantez:
  1. Bit op + comparison:
    (x & 1) == 1
  2. Bit op + bit op (& ve |):
    (a & b) | c
    — "a AND b ile or c" mu, "a AND (b or c)" mu? Belirsiz olmasın.
  3. and/or karışık:
    (x or y) and z
  4. Walrus + comparison:
    (n := len(x)) > 10
  5. Nested ternary: kullanma, elif yaz.
  6. Üs alma + minus:
    (-2) ** 2
    veya
    -(2 ** 2)
    — niyetin neyse.
Diğer yerlerde Python'un default precedence'ı insan sezgisine yakın — parantez ekleyince clutter.

Debug aracı —
ast
modülü ile precedence görmek#

Operator precedence'ı şüpheliysen Python sana parse tree'yi göstermeye razı:
import ast # Şu ifade nasıl parse oluyor? code = "x & 1 == 1" tree = ast.parse(code, mode="eval") print(ast.dump(tree, indent=2))
Çıktı:
Expression( body=BinOp( left=Name(id='x'), op=BitAnd(), right=Compare( left=Constant(value=1), ops=[Eq()], comparators=[Constant(value=1)] ) ) )
Yani:
x & (1 == 1)
— beklediğin değil!
ast.dump
ifadenin gerçek parse tree'sini gösteriyor. Şüphe duyduğunda kullan.

Daha okunabilir: ast.unparse#

# Python 3.9+ import ast code = "x & 1 == 1" tree = ast.parse(code, mode="eval") # ast.unparse aynı ifadeyi yazıyor — ama parse tree'sine göre # Ama bu örnekte aynı görünüyor print(ast.unparse(tree)) # x & 1 == 1 # Daha karmaşık: code = "a or b and c" tree = ast.parse(code, mode="eval") print(ast.unparse(tree)) # a or b and c — Python original # Anlamı: a or (b and c) — and > or
ast modülü zaten Python parser'ının kendisi. Şüpheye düştüğünde
ast.parse + ast.dump
ile gerçeği gör.

Bu derste neler kazandın?#

18 seviyeli precedence tablosu — hangi operatör hangi sırada.
Sağ assosiyatif olanlar:
**
ve assignment.
==
precedence'ı bit op'lardan yüksek
— bit + comparison'da parantez şart.
and
>
or
— mantıksal karışımda parantez.
-2 ** 2 = -4
klasik tuzağı (üs > unary).
Comparison chaining — özel kural, normal binary değil.
6 'mutlaka parantez' durumu.
ast.parse + ast.dump
debugging aracı.
Sıradaki ders: Type conversion (cast) —
int()
,
float()
,
str()
,
bool()
,
list()
,
tuple()
,
set()
,
dict()
,
bytes()
. Hangi dönüşüm hata atar, hangi otomatik (implicit),
__int__
/
__float__
/
__str__
magic methods, ve NumPy/pandas dünyasında dtype dönüşümleri.

Sık Sorulan Sorular

Hayır. **Bilmen gereken**: 5-6 yaygın kuralı (aritmetik sırası, ** sağ assosiyatif, == > & |, and > or). Geri kalanları parantez ile çöz. Tabloyu cep referansı olarak tut; ihtiyacın olan zaman bak.

Yorumlar & Soru-Cevap

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

İlgili İçerikler