פוסט
אופרטור השארית % ב-Python
מה % עושה ב-Python ואיפה הוא שימושי: זוגי או אי-זוגי, כל פריט N-י, חזרה להתחלה ברשימה או בשעון, פירוק שניות עם divmod, ספרות, FizzBuzz, ואיך הוא מתנהג עם מספרים שליליים.
% הוא אופרטור השארית (modulo): מה שנשאר אחרי חילוק. הפוסט על פעולות על מספרים הציג אותו בכמה שורות.
כל הדוגמאות הורצו עם Python 3.13 ונבדקו מול התיעוד של פעולות חשבון בינאריות והתיעוד של divmod().
מה % עושה
a % b הוא מה שנשאר כשמחלקים את a לקבוצות של b:
print(17 % 5, 10 % 2, 3 % 7, 0 % 4)
2 0 3 0
17 הן שלוש קבוצות של 5 ועוד 2. 3 קטן מ-7, אז כולו נשאר.
% הולך יד ביד עם חילוק שלמים //. התיעוד נותן את הזהות x == (x//y)*y + (x%y): המנה כפול המחלק, ועוד השארית, מחזירים את המספר המקורי.
זוגי או אי-זוגי
מספר הוא זוגי כשחילוק שלו ב-2 לא משאיר כלום:
number = 7
if number % 2 == 0:
print(number, "is even")
else:
print(number, "is odd")
7 is odd
אותו רעיון עובד עם כל מחלק. n % 3 == 0 פירושו “n מתחלק ב-3”.
כל פריט N-י
בתוך לולאה, % בוחר כל צעד N-י:
for i in range(1, 11):
if i % 3 == 0:
print(i, end=" ")
print()
3 6 9
בקוד אמיתי זה “לשמור כל 100 שורות” או “להדפיס התקדמות כל 1,000 פריטים”: if i % 1000 == 0:.
חזרה להתחלה
התוצאה של x % n תמיד בין 0 ל-n - 1 (כש-n חיובי). זה מושלם לתנועה במעגל, כמו ימות השבוע:
days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
today = 5
print(days[(today + 3) % 7])
print(days[(today - 8) % 7])
Mon
Thu
היום הוא אינדקס 5 (שישי). שלושה ימים אחר כך זה אינדקס 8, ו-8 % 7 הוא 1, יום שני. שמונה ימים אחורה זה אינדקס -3, ו--3 % 7 הוא 4, יום חמישי. האינדקס אף פעם לא יוצא מהרשימה. שעון של 24 שעות עובד באותה צורה.
%= שומר זווית בין 0 ל-359:
angle = 350
angle += 20
angle %= 360
print(angle)
10
פירוק יחידות עם divmod
כדי להפוך שניות לשעות, דקות ושניות, צריך גם את המנה וגם את השארית. divmod(x, y) מחזירה את שתיהן כזוג, (x // y, x % y):
total_seconds = 3725
minutes, seconds = divmod(total_seconds, 60)
hours, minutes = divmod(minutes, 60)
print(f"{hours}:{minutes:02}:{seconds:02}")
1:02:05
:02 משלים באפס לשתי ספרות, כמו שמוסבר בפוסט על f-strings.
ספרות של מספר
% 10 נותן את הספרה האחרונה של מספר שלם. % 100 נותן את שתי האחרונות. בשילוב עם // מקבלים כל ספרה:
n = 4096
print(n % 10, n % 100, n // 10 % 10)
6 96 9
FizzBuzz
תרגיל הראיונות בודק את % ואת הסדר של elif: מדפיסים את המספרים מ-1 עד 15, אבל “Fizz” לכפולות של 3, "Buzz" לכפולות של 5, ו-“FizzBuzz” לכפולות של שניהם:
for n in range(1, 16):
if n % 15 == 0:
print("FizzBuzz")
elif n % 3 == 0:
print("Fizz")
elif n % 5 == 0:
print("Buzz")
else:
print(n)
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz
(הפלט מוצג בשורה אחת כדי לחסוך מקום.) הבדיקה של n % 15 חייבת לבוא ראשונה, אחרת 15 יתאים ל-n % 3 == 0 וידפיס “Fizz”, כמו שמוסבר בפוסט על if/elif.
מספרים שליליים
כאן Python שונה מ-C, Java ו-JavaScript. לפי התיעוד, התוצאה של % “תמיד מקבלת את הסימן של האופרנד השני (או אפס)”:
print(-7 % 3, 7 % -3, -7 % -3)
print(-7 // 3, -7 == (-7 // 3) * 3 + (-7 % 3))
import math
print(math.fmod(-7, 3))
2 -2 -1
-3 True
-1.0
-7 // 3 מעוגל למטה ל--3, ו--3 * 3 + 2 הוא -7, ולכן השארית היא 2. ב-C או ב-JavaScript, -7 % 3 הוא -1. math.fmod() נותנת את ההתנהגות הזו, והתיעוד של math ממליץ על fmod() למספרים עשרוניים ועל % למספרים שלמים.
הכלל של Python הוא הסיבה שהטריק של חזרה להתחלה עובד גם עם אינדקסים שליליים, ושבדיקת n % 2 == 1 נכונה לאי-זוגיות גם כש-n שלילי:
print(-3 % 2 == 1, -3 % 2)
True 1
ב-C, -3 % 2 הוא -1, ולכן אותה בדיקה הייתה נכשלת.
מספרים עשרוניים
% עובד גם על float, אבל שגיאות עיגול יכולות להופיע בתוצאה:
print(7.5 % 2, 5 % 0.1)
1.5 0.09999999999999973
5 % 0.1 אינו 0, כי אי אפשר לשמור את 0.1 בדיוק. עדיף להשתמש ב-% עם מספרים שלמים כשאפשר.
טעויות נפוצות
- חילוק באפס.
5 % 0מעלהZeroDivisionError: integer modulo by zero, בדיוק כמו5 / 0. - בלבול בין
%לאחוזים. 50 % 10הוא 0, לא “5”. לאחוזים מכפילים:part / total * 100. - בלבול בין
%על מחרוזות לבין שארית. "%s is %d years old" % ("Dana", 30)הוא עיצוב המחרוזות הישן. כשמשמאל יש מחרוזת,%מעצב טקסט. היום משתמשים ב-f-strings.
מה הלאה
הצעד הבא בסדרה: לולאות עם for ו-while, שם % ותנאים עושים את רוב העבודה.