פוסט

אופרטורים לוגיים ב-Python: and, or, not

איך and, or ו-not מחברים תנאים ב-Python: טבלאות אמת, הערכה מקוצרת, למה and ו-or מחזירים את אחד האופרנדים, סדר קדימויות, ו-any() ו-all().

השוואה אחת עונה על שאלה אחת: האם המשתמש בגיר? תנאים אמיתיים שואלים לעיתים קרובות כמה שאלות בבת אחת: האם המשתמש בגיר וגם יש לו כרטיס? ל-Python יש לזה שלושה אופרטורים לוגיים: and, or ו-not. אלה מילים, לא סימנים כמו && ו-|| בשפות אחרות.

כל דוגמה כאן הורצה עם Python 3.13. ההתנהגות נבדקה מול התיעוד של פעולות בוליאניות והתיעוד של בדיקת ערך אמת.

שלושת האופרטורים

  • ‏a and b אמיתי כששני הצדדים אמיתיים.
  • ‏a or b אמיתי כשלפחות צד אחד אמיתי.
  • ‏not a הופך את הערך.
age = 25
has_ticket = True
print(age >= 18 and has_ticket)
print(age < 12 or age > 65)
print(not has_ticket)
True
False
False

אלה כל ארבעת הצירופים של שני ערכים:

for a in (True, False):
    for b in (True, False):
        print(a, b, a and b, a or b)
True True True True
True False False True
False True False True
False False False False

הם הופכים משפטי if מקוננים לשטוחים יותר. בדיקת הכרטיס מהפוסט הקודם הופכת ל:

age = 20
has_ticket = True
if age >= 18 and has_ticket:
    print("Come in")
Come in

הערכה מקוצרת

Python מחשבת משמאל לימין ועוצרת ברגע שהתשובה ידועה. אם הצד השמאלי של and שקרי, התוצאה שקרית, ולכן הצד הימני לא רץ בכלל. אם הצד השמאלי של or אמיתי, הצד הימני לא רץ:

def check(label, result):
    print("checking", label)
    return result

print(check("a", False) and check("b", True))
print(check("c", True) or check("d", True))
checking a
False
checking c
True

זה עובד כשומר: שמים את הבדיקה הבטוחה ראשונה, והבדיקה המסוכנת רצה רק כשזה בטוח:

items = []
if items and items[0] == "x":
    print("starts with x")
else:
    print("empty or not x")

count = 0
total = 10
if count != 0 and total / count > 2:
    print("big")
print("no ZeroDivisionError")
empty or not x
no ZeroDivisionError

‏items[0] על רשימה ריקה היה מעלה IndexError, ו-total / count היה מעלה ZeroDivisionError. אף אחד מהם לא רץ, כי הצד השמאלי כבר שקרי.

and ו-or מחזירים ערך, לא רק True או False

התיעוד מדויק: x and y מחזיר את x אם x שקרי, ואחרת את y. ‏x or y מחזיר את x אם x אמיתי, ואחרת את y. הם מחזירים את אחד האופרנדים, לא בהכרח bool:

print(0 and 5, 3 and 5, 0 or 5, 3 or 5)
print("" or "guest", [] or "empty", None and 1)
print(not "", not "hi", not 0)
0 5 5 3
guest empty None
True False True

‏not שונה: הוא תמיד מחזיר True או False.

זה הופך את or לנוח לערכי ברירת מחדל. תשובה ריקה מ-input() היא שקרית, ולכן or מחליף אותה. כאן המשתמש לחץ Enter בלי להקליד כלום:

name = input("Name: ") or "guest"
print("Hello", name)
Name: 
Hello guest

זהירות עם מספרים: count or 10 מחליף גם 0 אמיתי. כש-0 הוא ערך תקין, כותבים 10 if count is None else count.

סדר קדימויות

‏not נקשר הכי חזק, אחריו and, ואחריו or. שלושתם באים אחרי השוואות, ולכן age >= 18 and has_ticket לא צריך סוגריים:

print(True or False and False)
print((True or False) and False)
print(not 1 == 2)
True
False
True

השורה הראשונה היא True or (False and False). כשמערבבים and ו-or, כדאי להוסיף סוגריים גם כשלא חייבים. מי שקורא את הקוד לא צריך לזכור את הטבלה.

מלכודת נפוצה היא השוואה של משתנה אחד לשני ערכים:

x = 5
print(x == 3 or 4)
print(x == 3 or x == 4)
print(x in (3, 4))
4
False
False

‏x == 3 or 4 פירושו (x == 3) or 4. ההשוואה שקרית, אז or מחזיר 4, שהוא ערך אמיתי. ‏if עם התנאי הזה ירוץ תמיד. חוזרים על ההשוואה, או עדיף, משתמשים ב-in. לטווח, שרשור השוואות כמו 10 < x < 20 קצר יותר משתי השוואות עם and.

הכול ביחד: שנה מעוברת

בלוח הגרגוריאני, שנה היא מעוברת אם היא מתחלקת ב-4, חוץ משנים שמתחלקות ב-100, אלא אם הן מתחלקות גם ב-400:

def is_leap(year):
    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)

print(is_leap(2024), is_leap(1900), is_leap(2000), is_leap(2026))
True False True False

הסוגריים חשובים כאן. ‏% הוא אופרטור השארית, שמקבל פוסט משלו בהמשך הסדרה.

any() ו-all()

כשיש הרבה תנאים, הפונקציות המובנות any() ו-all() הן ה-or וה-and של אוסף שלם:

scores = [72, 88, 95]
print(any(s > 90 for s in scores), all(s > 70 for s in scores))
print(all([]), any([]))
True True
True False

‏any מחזירה True אם לפחות פריט אחד אמיתי. ‏all מחזירה True אם כל הפריטים אמיתיים. שימו לב למקרים הריקים: all([]) הוא True ו-any([]) הוא False. שתיהן עוצרות מוקדם, כמו and ו-or. ראו את התיעוד של any() ושל all().

טעויות נפוצות

  • כתיבת &&, || או !. אלה משפות אחרות. ‏Python משתמשת ב-and, or ו-not. ‏!x הוא SyntaxError.
  • שימוש ב-& ו-| במקום and ו-or. ‏& ו-| הם אופרטורים על ביטים. ‏1 & 2 הוא 0, בעוד 1 and 2 הוא 2. הם גם לא עוצרים מוקדם.

מה הלאה

בפוסט הבא: הזחה, ושגיאת IndentationError שמופיעה כשהבלוקים מתחת לתנאים לא מוזחים נכון.