פייתון

חוברת הקורס

הכל בעמוד אחד, להדפסה

כל התוכן בעמוד אחד. כפתור ההדפסה למטה שומר כ-PDF, וכל פרק מתחיל בעמוד חדש.

יחידה 2 — משתנים

מפת היחידה

פרק דקה מה קורה שם
2.1 0:55 מה זה משתנה, והשמה עם =
2.1 1:40 משתנה הוא מצביע, לא קופסה
2.1 2:22 הטיפוסים str, int, float
2.1 4:31 לכתוב טוב: לא להחליף טיפוס של משתנה
2.1 5:35 חישוב ממשתנים אחרים
2.2 0:21 הטיפוס הבוליאני
2.2 1:08 ביטוי לוגי כערך
2.2 2:41 ==, !=, not, and, or
2.2 4:43 לכתוב טוב: רווחים וסוגריים בביטוי ארוך
2.2 5:56 האופרטור in
2.2 6:23 help(), ואיך יוצאים ממנו
2.3 0:44 קלט מהמשתמש עם input
2.3 2:43 TypeError: הקלט הוא תמיד מחרוזת
2.3 3:17 הסבה בין טיפוסים
2.3 4:09 החלפת ערכים בשורה אחת
2.4 0:33 קונבנציות כתיבה ו-PEP 8
2.4 2:20 פייתון רגישה לגודל אות
2.4 2:57 אותיות קטנות, קו תחתון, וקבועים
2.4 3:59 שמות משמעותיים

2.1 · משתנה הוא מצביע

משתנה הוא שם שמצביע על ערך. כותבים את השם, סימן שווה, ואת הערך:

>>> course = "Python"
>>> unit_name = 'variables'
>>> unit_number = 2
>>> chapter_length = 7.25
>>> course
'Python'

כדי לראות ערך של משתנה באינטרפרטר, מקלידים את השם ולוחצים Enter.

מה קורה בזיכרון. course = "Python" יוצר קודם אובייקט שמכיל את הטקסט, ורק אחר כך משתנה בשם course שמצביע עליו. זו ההבחנה שהפרק חוזר עליה פעמיים: המשתנה אינו הערך. הוא מצביע אליו.

ארבעה טיפוסים

כל אובייקט בפייתון הוא מטיפוס מסוים:

ערך טיפוס מה זה
"Python", 'variables' str מחרוזת. מרכאות כפולות או יחידות, שתיהן תקינות
2 int מספר שלם
7.25, 7.0 float מספר עשרוני. גם 7.0 הוא float
True, False bool בוליאני. מגיע בפרק 2.2

לא מצהירים על הטיפוס. פייתון מזהה אותו לבד מהערך, וזה מה שנקרא dynamically typed.

שינוי ערך

"לשנות" משתנה זה להעביר אותו להצביע על אובייקט אחר:

>>> chapter_length = 7.25
>>> chapter_length = "7.25 minutes"
>>> chapter_length
'7.25 minutes'

לכתוב טוב: הדוגמה הזו עובדת, אבל היא bad practice. המשתנה התחיל כ-float ועבר להצביע על str. פעולה שמתאימה לטיפוס אחד לא בהכרח מתאימה לשני, וכך נוצרות שגיאות שקשה למצוא. משתנה נשאר באותו טיפוס.

חישוב ממשתנים

>>> current_unit = unit_number
>>> next_unit = current_unit + 1
>>> next_unit
3
>>> print(current_unit - 1)
1

אחרי current_unit = unit_number שני המשתנים מצביעים על אותו אובייקט, 2. ההדפסה בסוף היא כמה יחידות כבר הסתיימו: היחידה הנוכחית עוד לא נגמרה, ולכן מפחיתים אחת.

2.2 · בוליאני וביטויים לוגיים

לטיפוס bool יש שני ערכים בלבד: True ו-False. שימו לב לאות הגדולה, true בלי אות גדולה הוא שגיאה.

>>> i_am_hungry = True

ביטוי לוגי כערך

במקום לכתוב True או False ישירות, אפשר להשים תוצאה של ביטוי:

>>> number_of_sandwiches = 2
>>> i_am_hungry = number_of_sandwiches < 4
>>> i_am_hungry
True

כל מספר שאינו אפס נחשב True, ואפס הוא תמיד False. זה ייראה שימושי כשיגיעו הלולאות.

>>> bool(5)
True
>>> bool(0)
False

האופרטורים

>>> number_of_sandwiches == 5
False
>>> number_of_sandwiches != 5
True
>>> not number_of_sandwiches != 5
False

== הוא השוואה, ו-= הוא השמה. מי שמבלבל ביניהם יבלבל עוד הרבה פעמים, ולכן כדאי לעצור על זה עכשיו.

and דורש ששני הצדדים יהיו נכונים, ו-or מסתפק באחד:

>>> cups_of_coffee = 3
>>> number_of_sandwiches < 4 and cups_of_coffee > 5
False
>>> number_of_sandwiches < 4 or cups_of_coffee > 5
True

לכתוב טוב: ביטוי ארוך

ביטוי שבנוי משלושה תנאים: רעב אם אכלתי לפחות כריך אחד וגם פחות מארבעה, או בדיוק שתי קערות מרק. שני שינויים הופכים אותו לקריא: רווח משני צדי כל אופרטור, וסוגריים סביב כל תנאי.

>>> bowls_of_soup = 0
>>> i_am_hungry = (((number_of_sandwiches >= 1)
...                 and (number_of_sandwiches < 4))
...                or (bowls_of_soup == 2))
>>> i_am_hungry
True

in

בודק אם מחרוזת אחת נמצאת בתוך אחרת:

>>> "break" in "breakfast"
True
>>> "b" in "food"
False

בקשת עזרה

אף אחד לא זוכר את כל הפקודות. מה שצריך לזכור הוא איפה מחפשים.

  • help() פותח את ספריית העזרה של פייתון. שם מקלידים, למשל, in ומקבלים הסבר מלא.
  • רווח מדפדף, ו-q יוצא. בלי זה אפשר להיתקע שם.
  • help("in") הולך ישר לנושא, בלי להיכנס לממשק.
  • באינטרנט יש תיעוד רב, אבל רק לפייתון 3. זו הגרסה של הקורס, וחלק מההסברים ברשת עוד נכתבו לגרסה 2.

2.3 · קלט, הסבה והחלפה

input

input עוצר את התוכנית ומחכה שהמשתמש יקליד ויקיש Enter. מה שבסוגריים מוצג לו על המסך:

name = input("What is your name? ")
print(name)

הקיצור _: באינטרפרטר, המשתנה _ מחזיק את התוצאה האחרונה. אם הרצתם input(...) בלי לשמור, _ עדיין מכיל את מה שהוקלד. זה עובד רק באינטרפרטר, לא בקובץ.

התקלה המרכזית של הפרק

>>> number = "5"
>>> print(number ** 2)
Traceback (most recent call last):
  ...
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

בשורה הראשונה "5" במקום input(), כי זה בדיוק מה ש-input מחזיר: input מחזיר תמיד מחרוזת, גם כשהוקלד מספר. ואי אפשר להעלות מחרוזת בחזקה.

הסבה (casting)

כותבים את הטיפוס הרצוי, ובסוגריים את הערך:

>>> int(number) ** 2
25
>>> float(number)
5.0

החלפת ערכים בשורה אחת

>>> peter_pan_home = "Neverland"
>>> snow_white_home = "the forest"
>>> peter_pan_home, snow_white_home = snow_white_home, peter_pan_home
>>> peter_pan_home
'the forest'
>>> snow_white_home
'Neverland'

בשפות כמו C ו-Java החלפה דורשת משתנה זמני ושלוש שורות.

2.4 · שמות למשתנים

קונבנציות כתיבה הן מוסכמות של הקהילה על איך קוד נראה. פייתון לא אוכפת אותן, והקוד ירוץ גם בלעדיהן. המסמך שמרכז אותן הוא PEP 8, והקורס מצפה לעבוד לפיו.

פייתון רגישה לגודל אות

>>> nose_length = 4
>>> Nose_length = 6
>>> Nose_length - nose_length
2

אלה שני משתנים שונים (case-sensitive), והקוד תקין. אבל מי שקורא אותו לא יודע מה ההבדל ביניהם. כאן הקונבנציות נכנסות.

הכללים

כלל דוגמה
אותיות קטנות character = "Pinocchio"
יותר ממילה אחת: קו תחתון ביניהן nose_length
קבוע, ערך שלא משתנה בתוכנית: אותיות גדולות PI = 3.14
שם שאומר מה יש בו fairy_tale_file_with_extension, ולא file

קבוע הוא קונבנציה בלבד. אם משנים את PI באמצע התוכנית, פייתון לא תזרוק שגיאה. האחריות על המתכנת.

שמות כלליים כמו number או name עובדים, אבל לא אומרים כלום. שם ארוך ומפורט עדיף, כי הוא חוסך לקורא את הניחוש.

בדיקה עצמית

  1. אחרי a = 5 ואז b = a, על מה מצביע b?
  2. מה הטיפוס של "2" + "2", ומה הערך?
  3. מה התוצאה של not 0?
  4. המשתמש הקליד 7 ב-x = input(). מה יחזיר x * 2?
  5. איזה שם עדיף לפי PEP 8: Total, total_price או tp?

תשובות. 1: על אותו אובייקט, 5. שני שמות, ערך אחד. 2: str, והערך '22'. 3: True, כי 0 נחשב False. 4: '77'. הקלט הוא מחרוזת, וכפל מחרוזת במספר משכפל אותה. כדי לקבל 14 צריך int(x) * 2. 5: total_price. אותיות קטנות, קו תחתון, ושם שאומר מה יש בו.

יחידה 3 — מחרוזות

מפת היחידה

פרק דקה מה קורה שם
3.1 0:23 מה זו מחרוזת
3.1 1:01 השגיאה הראשונה: מרכאות שלא תואמות
3.1 2:50 גרש באמצע מחרוזת
3.1 4:16 הלוכסן ההפוך, \
3.1 4:47 שרשור עם + והכפלה עם *
3.1 5:26 str()
3.2 0:46 \n ו-\t
3.2 1:27 נתיב קובץ שנשבר
3.2 2:37 מרכאות משולשות
3.3 1:01 אינדקסים, מאפס
3.3 1:41 אינדקס שלילי
3.3 2:06 IndexError
3.3 2:52 חיתוך (slicing)
3.3 3:42 להשמיט התחלה או סוף
3.3 5:18 חיתוך בקפיצות
3.3 6:04 מחרוזת לא ניתנת לשינוי
3.4 0:58 למה פונקציות
3.4 1:48 math.sqrt
3.4 2:25 פונקציה מול מתודה
3.4 3:11 upper ו-zfill
3.4 4:40 dir
3.4 5:04 לכתוב טוב: רווחים

3.1 · יצירת מחרוזות

מחרוזת (str) היא רצף של תווים, אפילו תו אחד, בתוך מרכאות. המרכאות אומרות לפייתון שזה טקסט, ולא שם של פקודה או של משתנה.

השגיאה הראשונה

>>> 'I really hope defining this string will work"
Traceback (most recent call last):
  ...
SyntaxError: unterminated string literal (detected at line 1)

איך קוראים את ההודעה. ה-Traceback אומר באיזו שורה פייתון נתקעה, סמן קטן מצביע על המקום, והשורה האחרונה אומרת מה סוג השגיאה.

כאן זו שגיאת תחביר, SyntaxError: הקוד לא בנוי לפי חוקי השפה. המחרוזת נפתחה במרכאה יחידה, ופייתון הגיעה לסוף השורה בלי למצוא את המרכאה הסוגרת.

בסרטון ההודעה מופיעה כ-EOL while scanning string literal, ניסוח של גרסאות ישנות יותר. בגרסה עדכנית תופיע ההודעה שלמעלה. המשמעות זהה.

הכלל: כפולות או יחידות, שתיהן בסדר, כל עוד אותה מחרוזת נפתחת ונסגרת באותו סוג.

גרש באמצע

>>> 'What's wrong here?'
Traceback (most recent call last):
  ...
SyntaxError: unterminated string literal (detected at line 1)

פייתון קוראת את המחרוזת 'What', ונתקעת באות s שאחריה. ההודעה בסרטון הייתה invalid syntax, ושוב, זה ניסוח של גרסה אחרת.

שלושה פתרונות:

>>> "What's wrong here?"
"What's wrong here?"
>>> 'She said "maybe" and left'
'She said "maybe" and left'
>>> print("She said \"don't\" twice")
She said "don't" twice
  1. גרש בפנים: עוטפים במרכאות כפולות
  2. מרכאות כפולות בפנים: עוטפים ביחידות
  3. גם וגם: לוכסן הפוך (\, backslash) לפני התו הבעייתי. השם שלו הוא escape character, כי הוא "מבריח" את התו שאחריו מהמשמעות הרגילה שלו

שרשור והכפלה

>>> first = "We "
>>> second = "belong "
>>> third = "together"
>>> first + second + third
'We belong together'
>>> duplicate_me = ":) "
>>> duplicate_me * 8
':) :) :) :) :) :) :) :) '

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

str()

>>> x = 3
>>> type(x)
<class 'int'>
>>> x + x
6
>>> x = str(x)
>>> type(x)
<class 'str'>
>>> x + x
'33'

אותו +, שתי תוצאות. על מספרים זה חיבור, ועל מחרוזות זה שרשור.

3.2 · תווים מיוחדים

תו מה עושה
\n שורה חדשה
\t הזחה (טאב)
\\ לוכסן הפוך אחד, כמו שהוא

טבלת תוצאות במחרוזת אחת

>>> rounds_and_winners = "Round\tWinner\n1\tA\n2\tB\n3\tA"
>>> print(rounds_and_winners)
Round   Winner
1   A
2   B
3   A

שימו לב: רק print מציג את השורות והטאבים. בלי print האינטרפרטר מראה את המחרוזת עם \n ו-\t כמו שהם.

כשזה מפריע: נתיב של קובץ

>>> print("C:\tournaments\new_scores.txt")
C:  ournaments
ew_scores.txt

פייתון ראתה \t ו-\n והדפיסה טאב ושורה חדשה. הפתרון: לוכסן הפוך נוסף, שמנטרל את המיוחד:

>>> print("C:\\tournaments\\new_scores.txt")
C:\tournaments\new_scores.txt

מרכאות משולשות

שלוש מרכאות בהתחלה ושלוש בסוף. אפשר לשבור שורות ב-Enter, בלי \n, ואפשר לכתוב גרש בפנים בלי בעיה:

>>> how_to_win = """Three steps to win at chess:
... Don't lose your queen
... Control the center
... Checkmate"""
>>> print(how_to_win)
Three steps to win at chess:
Don't lose your queen
Control the center
Checkmate

... הוא האינטרפרטר שמחכה להמשך. לא מקלידים אותו.

שימוש נוסף, שיגיע בהמשך הקורס: תיעוד של קוד, הסבר מפורט על מה שקטע קוד עושה.

3.3 · גישה למחרוזת

אינדקס

גישה לתו היא לפי המיקום שלו, ולא לפי הערך. סופרים מאפס, ומבקשים בסוגריים מרובעים:

>>> card_str = "joker"
>>> card_str[0]
'j'
>>> card_str[1]
'o'
תו j o k e r
מההתחלה 0 1 2 3 4
מהסוף -5 -4 -3 -2 -1
>>> card_str[-1]
'r'
>>> card_str[-5]
'j'

מעבר לגבול

>>> card_str[5]
Traceback (most recent call last):
  ...
IndexError: string index out of range

ל-"joker" יש חמישה תווים, ולכן המיקום האחרון הוא 4. גם card_str[-6] ייתן את אותה שגיאה. הטיפ: לבדוק את האורך לפני. איך, יגיע בהמשך.

חיתוך

[התחלה:סוף]. ההתחלה נכללת, הסוף לא:

>>> game = "gaming"
>>> game[0:2]
'ga'
>>> game[2:5]
'min'

סוף פחות התחלה הוא אורך המקטע. 5 - 2 הם שלושה תווים.

להשמיט: בלי התחלה, פייתון מתחילה מ-0. בלי סוף, היא ממשיכה עד הסוף. בלי שניהם, מתקבלת כל המחרוזת:

>>> game[:2]
'ga'
>>> game[2:]
'ming'
>>> game[:]
'gaming'
>>> game[:-1]
'gamin'

[:-1] הוא הכל חוץ מהתו האחרון.

בקפיצות

נקודתיים שלישיות, וגודל הקפיצה:

>>> game[0:5:2]
'gmn'
>>> game[1::2]
'aig'

כך מקבלים את כל המיקומים הזוגיים, או האי-זוגיים, בלי לולאה.

אי אפשר לשנות מחרוזת

>>> my_string = "Change me"
>>> my_string[0] = "S"
Traceback (most recent call last):
  ...
TypeError: 'str' object does not support item assignment

מחרוזת היא immutable, לא ניתנת לשינוי. המונח הזה יחזור.

מה כן עושים: יוצרים מחרוזת חדשה עם השינוי, ומעבירים את המשתנה להצביע עליה:

>>> my_string = "S" + my_string[1:]
>>> my_string
'Shange me'

זה בדיוק מה שקורה בכל "שינוי" של משתנה מחרוזת: ההצבעה עוברת למחרוזת חדשה, והישנה לא נגעה.

3.4 · פונקציות ומתודות

למה בכלל. ביטוי שיש בו חמישה שורשים ריבועיים פירושו לכתוב חמש פעמים את הקוד שמחשב שורש. ואם יש בו טעות, מתקנים חמש פעמים. פונקציה נכתבת פעם אחת, ונקראת כמה פעמים שצריך.

פונקציה

שם, סוגריים, ובתוכם הפרמטרים, מופרדים בפסיקים:

>>> import math
>>> math.sqrt(9)
3.0
>>> math.sqrt(16) + math.sqrt(25)
9.0

import math טוען את הספרייה, ואז math.sqrt קורא לפונקציה מתוכה. שימו לב שהתוצאה היא 3.0 ולא 3: sqrt מחזירה תמיד float.

פונקציות משלכם מגיעות ביחידה 5.

מתודה

פונקציה ששייכת לטיפוס מסוים. קודם האובייקט, אחר כך נקודה, השם וסוגריים:

>>> "print me in upper case".upper()
'PRINT ME IN UPPER CASE'
>>> "1".zfill(5)
'00001'
>>> "234".zfill(5)
'00234'
  • upper בלי פרמטרים. הסוגריים נכתבים בכל זאת, ריקים
  • zfill(רוחב) מרפדת באפסים משמאל עד הרוחב שביקשתם

הנקודה מטעה. ב-math.sqrt היא אומרת "מתוך הספרייה". ב-"1".zfill היא אומרת "המתודה של האובייקט הזה". ומאחורי הקלעים, האובייקט עצמו נשלח למתודה בתור הפרמטר הראשון.

איך מוצאים מתודות

dir(str)

מחזיר רשימה ארוכה של כל מה שאפשר לעשות עם מחרוזת. upper ו-zfill נמצאות בסוף.

לכתוב טוב: רווחים

כלל נכון לא
רווח אחד אחרי כל פסיק f(a, b, c) f(a,b,c)
פרמטר אחד: בלי רווחים בכלל f(a) f( a )
רווח משני צדי אופרטור x = 1 + 2 x=1+2

בדיקה עצמית

  1. מה ההבדל בין print("a\tb") לבין הקלדת "a\tb" באינטרפרטר?
  2. עם s = "python": מה זה s[-3:]?
  3. מה זה s[::-1]? (לא הופיע בסרטון. נסו לנחש לפי הכלל.)
  4. איך הופכים את "cat" ל-"bat"?
  5. מה ההבדל בין math.sqrt(x) לבין x.upper()?

תשובות. 1: print מציג טאב אמיתי. האינטרפרטר מציג את המחרוזת כמו שהיא, עם \t. 2: 'hon'. 3: 'nohtyp'. קפיצה שלילית עוברת על המחרוזת מהסוף להתחלה. 4: "b" + "cat"[1:]. אי אפשר לשנות תו בתוך מחרוזת, אז בונים חדשה. 5: sqrt היא פונקציה מתוך הספרייה math. upper היא מתודה של מחרוזות, והאובייקט שלפני הנקודה הוא מה שהיא פועלת עליו.

יחידה 4 — תנאים

מפת היחידה

פרק דקה מה קורה שם
4.0 0:00 אינטרפרטר מול עורך טקסט
4.0 0:34 קובץ .py והרצה
4.1 0:00 מה זה בלוק
4.1 0:31 שורת פקודה ונקודתיים
4.1 1:03 הזחה: ארבעה רווחים
4.1 1:34 IndentationError
4.1 2:43 pass
4.2 0:32 if ובקרת זרימה
4.2 1:36 else
4.2 1:57 elif
4.2 3:07 למה לא תנאי בתוך תנאי
4.2 3:30 תנאי עם and

4.0 · עורך טקסט

עד עכשיו הקוד נכתב באינטרפרטר. זה טוב לשורה אחת ולבדיקה מהירה, אבל תוכנית של עשרות שורות כותבים בעורך טקסט ושומרים כקובץ עם הסיומת .py.

אינטרפרטר עורך טקסט
למה שורות קצרות, ניסוי תוכנית שלמה
נשמר לא קובץ .py
פלט מיד אחרי כל שורה אחרי הרצה של כל הקובץ
ערך בלי print מוצג לא מוצג

השורה האחרונה בטבלה היא ההבדל שתופס הכי הרבה אנשים: באינטרפרטר "hi".upper() מציג 'HI'. בקובץ, בלי print, לא יוצג כלום.

כל עורך עובד, גם פנקס רשימות. עורך מתאים לתכנות צובע את הקוד, משלים שמות ומסמן טעויות. ההרצה תלויה בעורך. בקורס זה כפתור run.

מכאן והלאה הדוגמאות בקורס מגיעות בשתי הצורות: קצרות באינטרפרטר, וארוכות בעורך.

4.1 · בלוקים

בלוק הוא קבוצת שורות ששייכות לאותו רצף לוגי. שני חוקים:

  1. השורה הראשונה מסתיימת בנקודתיים. זו שורת הפקודה, כמו if.
  2. השורות של הבלוק מוזחות ביחס אליה. הקורס, כמו PEP 8, משתמש בארבעה רווחים.
>>> finished_chapter = True
>>> learners = 10
>>> if finished_chapter:
...     print("Well done!")
...     learners = learners + 1
...
Well done!
>>> learners
11

שתי השורות המוזחות רצות רק אם התנאי נכון. ההזחה היא מה שאומר לפייתון איפה הבלוק נגמר. בשפות אחרות עושים את זה בסוגריים מסולסלים. בפייתון זה הרווחים.

הזחה היא תחביר

>>> debugging = "oops"
>>>   print(debugging)
Traceback (most recent call last):
  ...
IndentationError: unexpected indent

רווחים בתחילת שורה שלא אמורה להיות מוזחת הם שגיאה, ולא עניין של סגנון. ולא מערבבים רווחים וטאבים באותו קובץ.

pass

בלוק לא יכול להיות ריק. כשכותבים שלד של תוכנית ועוד לא יודעים מה ייכנס לבלוק, כותבים pass, והתוכנית רצה:

>>> grade = 75
>>> if grade > 90:
...     print("Good job")
... else:
...     pass  # TODO: what to say below 90
...

ההערה שאחרי # היא תזכורת לעצמכם. פייתון מתעלמת ממנה.

4.2 · if, else, elif

תנאי מאפשר להחליט אילו שורות ירוצו. זה נקרא בקרת זרימה.

>>> weather = "sunny"
>>> if weather == "rainy":
...     print("Take an umbrella")
... elif weather == "sunny":
...     print("Take a hat and sunglasses")
... else:
...     print("Take a light jacket")
...
Take a hat and sunglasses
מתי רץ חובה?
if התנאי נכון כן, תמיד ראשון
elif כל מה שלפניו לא התקיים, והתנאי שלו כן לא. כמה שרוצים
else שום דבר לפניו לא התקיים לא. רק אחד, ותמיד אחרון

פייתון בודקת לפי הסדר ועוצרת בראשון שמתקיים. אם גם if וגם elif נכונים, רק הבלוק של if ירוץ.

בחירה של המשתמש

choice = input("Pick a number: ")
if choice == "1":
    print("You chose 1")
elif choice == "2":
    print("You chose 2")
elif choice == "3":
    print("You chose 3")
else:
    print("You chose another number")

שימו לב ל-"1" במרכאות: input מחזיר מחרוזת, כמו ביחידה 2. השוואה ל-1 בלי מרכאות לא תתקיים אף פעם.

למה elif ולא תנאי בתוך תנאי

אותה תוכנית בלי elif דורשת if בתוך else בתוך else, וכל רמה מוזחת עוד ארבעה רווחים. זה נקרא קינון (nesting), והקורס מגדיר אותו כמסורבל. elif שומר את כל האפשרויות באותה רמה.

תנאי מורכב

כל מה שלמדנו על and ו-or ביחידה 2 עובד גם כאן:

>>> months_since_last_eclipse = 18
>>> moon_position = 42
>>> sun_position = 42
>>> if ((months_since_last_eclipse == 18)
...         and (moon_position == sun_position)):
...     print("Solar eclipse!")
... else:
...     print("Just a regular day")
...
Solar eclipse!

מלכודת שהסרטון רק מזכיר: משתנה שמקבל ערך רק בתוך אחד הבלוקים לא קיים אם הבלוק הזה לא רץ. ניסיון להשתמש בו אחר כך נותן NameError. הפתרון הפשוט: לתת לו ערך לפני התנאי.

בדיקה עצמית

  1. x = 5. בתנאי הראשון x > 3 מדפיס A, וב-elif x > 1 מדפיס B. מה יודפס?
  2. מה חסר כאן: if x > 3 print("big")?
  3. אפשר לכתוב else בלי if לפניו?
  4. למה if input() == 5: לא יתקיים אף פעם?

תשובות. 1: רק A. פייתון עוצרת בתנאי הראשון שמתקיים, גם אם x > 1 נכון. 2: נקודתיים אחרי התנאי, ושורה חדשה מוזחת. 3: לא. else ו-elif באים תמיד אחרי if. 4: input מחזיר מחרוזת, ו-"5" אינו שווה ל-5.

יחידה 5 — פונקציות

מפת היחידה

פרק דקה מה קורה שם
5.1 0:00 למה פונקציות
5.1 1:05 def, וקריאה לפונקציה
5.1 1:39 פונקציה עם פרמטר
5.1 2:42 שם בלי סוגריים: פונקציה היא אובייקט
5.1 3:45 פרמטר מול ארגומנט
5.1 4:16 ארגומנט מהטיפוס הלא נכון
5.2 0:32 משתנה בתוך פונקציה ומחוצה לה
5.2 1:05 טווח הכרה: גלובלי ולוקאלי
5.2 2:11 NameError מחוץ לפונקציה
5.2 3:14 אותו שם בפנים ובחוץ
5.2 5:21 קריאת משתנה גלובלי, ולמה לא
5.2 5:53 global
5.3 0:35 return
5.3 2:10 פונקציה בלי return מחזירה None
5.3 3:16 החזרת כמה ערכים
5.3 4:20 ערכי ברירת מחדל
5.3 6:23 ברירת מחדל רק בסוף
5.3 7:25 ארגומנטים לפי שם
5.4 1:05 תיעוד (docstring)
5.4 3:21 help ו-__doc__
5.4 4:24 הפונקציה main
5.4 6:35 קוד לא קריא מול קוד קריא
5.4 8:17 ארבעה כללים לפונקציות

5.1 · לכתוב פונקציה

פונקציה היא רצף פקודות באותו בלוק, שמבצע משימה מוגדרת. כותבים אותה פעם אחת, נותנים לה שם, וקוראים לה כמה פעמים שצריך. קל יותר לשנות, לבדוק ולדבג. ביחידה 3 השתמשנו בפונקציות מוכנות. מכאן כותבים לבד.

>>> def my_first_function():
...     print("This is my first function in Python")
...
>>> my_first_function()
This is my first function in Python
  1. def (מ-define), שם הפונקציה, סוגריים ונקודתיים
  2. גוף הפונקציה מוזח, כמו כל בלוק
  3. הגדרה אינה הרצה. הפונקציה רצה רק כשקוראים לה, עם סוגריים

פרמטר

>>> def square(number):
...     print(number ** 2)
...
>>> square(3)
9

פונקציה עם פרמטר מצפה לקבל אותו בכל קריאה. בלעדיו:

>>> square()
Traceback (most recent call last):
  ...
TypeError: square() missing 1 required positional argument: 'number'

פרמטר מול ארגומנט

מה זה בדוגמה
פרמטר השם בהגדרה number
ארגומנט הערך שנשלח בקריאה 3

שם בלי סוגריים

>>> square
<function square at 0x...>

אין שגיאה, ושום דבר לא רץ. שם של פונקציה בלי סוגריים הוא כמו שם של משתנה: הוא מצביע על אובייקט, והאובייקט הוא הפונקציה עצמה. (המספר אחרי at הוא המיקום בזיכרון, ומשתנה בכל הרצה.)

ולכן אפשר להשים פונקציה למשתנה:

>>> func = square
>>> func(6)
36

הטיפוס של הארגומנט

>>> square("3")
Traceback (most recent call last):
  ...
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

פייתון לא בודקת טיפוסים מראש. השגיאה תופיע רק כשהשורה תרוץ, ויכולה להפיל את התוכנית באמצע. שליחת הטיפוס הנכון היא באחריות מי שקורא לפונקציה, ואת הטיפוס המצופה מוצאים בתיעוד שלה (פרק 5.4).

5.2 · טווח הכרה

טווח הכרה (scope) הוא האזור בתוכנית שבו משתנה קיים.

איפה הוגדר איפה מוכר
גלובלי בתוכנית, מחוץ לכל פונקציה בכל התוכנית
לוקאלי בתוך פונקציה רק בתוך הפונקציה
>>> fish_num = 9
>>> def fish_food():
...     food_for_fish = 5
...     print(food_for_fish * fish_num)
...
>>> fish_food()
45

fish_num גלובלי, ו-food_for_fish לוקאלי. כמו דגים: הם קיימים רק באקווריום.

לוקאלי מחוץ לפונקציה

>>> def ocean():
...     fish_view = "coral reef"
...     print(fish_view)
...
>>> ocean()
coral reef
>>> print(fish_view)
Traceback (most recent call last):
  ...
NameError: name 'fish_view' is not defined

אותו שם בפנים ובחוץ

>>> animal = "rabbit"
>>> def water():
...     animal = "goldfish"
...     print(animal)
...
>>> water()
goldfish
>>> print(animal)
rabbit

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

הפתרון: שמות שונים. land_animal ו-sea_animal, וכבר לא צריך לנחש.

לקרוא משתנה גלובלי מתוך פונקציה

זה עובד (ראינו את fish_num למעלה), אבל לא מומלץ, אלא אם זה קבוע. הדרך המסודרת להכניס ערך לפונקציה היא כארגומנט.

global

>>> animal = "rabbit"
>>> def water():
...     global animal
...     animal = "goldfish"
...     print(animal)
...
>>> water()
goldfish
>>> print(animal)
goldfish

global אומר לפייתון שהשם הוא של המשתנה הגלובלי, ולכן ההשמה משנה אותו. אפשרי, וממש לא מומלץ: פונקציה שמשנה משתנים מבחוץ קשה לבדוק ולהבין.

5.3 · קלט ופלט של פונקציה

return

print מציג ערך ושוכח אותו. return מחזיר אותו, ואפשר להמשיך לעבוד איתו.

>>> def num_of_oranges(small_cups, large_cups):
...     return small_cups + large_cups * 3
...
>>> oranges = num_of_oranges(8, 10)
>>> oranges
38

כוס קטנה היא תפוז אחד, וגדולה היא שלושה. oranges מצביע על הערך שהוחזר, 38, ולא על הפונקציה.

בלי return: None

>>> def greet_customers(small_cups, large_cups):
...     total = small_cups + large_cups
...     print("Thanks for ordering", total, "cups!")
...
>>> result = greet_customers(2, 1)
Thanks for ordering 3 cups!
>>> print(result)
None

כל פונקציה מחזירה משהו. בלי return, היא מחזירה את None, אובייקט שמשמעותו "כלום". return None ו-return לבד מחזירים את אותו דבר.

כמה ערכים

>>> def amount_of_oranges(small_cups, large_cups):
...     oranges = small_cups + large_cups * 3
...     kilograms = oranges / 5
...     return oranges, kilograms
...
>>> oranges_number, kilograms_to_buy = amount_of_oranges(15, 5)
>>> oranges_number
30
>>> kilograms_to_buy
6.0

מפרידים בפסיקים ב-return, ומקבלים לתוך כמה משתנים, כמו בהחלפת ערכים ביחידה 2. מאחורי הקלעים חוזר tuple, שמגיע ביחידה 8.

ערכי ברירת מחדל

>>> def amount_of_oranges(small_cups=20, large_cups=10):
...     oranges = small_cups + large_cups * 3
...     return oranges, oranges / 5
...
>>> amount_of_oranges()
(50, 10.0)
>>> amount_of_oranges(3, 4)
(15, 3.0)

ארגומנט עם ברירת מחדל נקרא גם אופציונלי: אפשר לשלוח אותו, ולא חובה.

ברירת מחדל רק בסוף:

>>> def amount_of_oranges(small_cups=20, large_cups):
...     pass
Traceback (most recent call last):
  ...
SyntaxError: parameter without a default follows parameter with a default

אם היה מותר, amount_of_oranges(5) היה דו-משמעי: האם 5 הולך לראשון או לשני? לכן פרמטרים עם ברירת מחדל באים אחרי כל אלה שבלי.

ארגומנטים לפי שם

>>> def profit(small_cups, large_cups,
...            small_price, large_price):
...     print(small_cups * small_price
...           + large_cups * large_price)
...
>>> profit(large_price=10, small_price=4,
...        large_cups=3, small_cups=5)
50

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

>>> profit(large_cups=3, small_price=4, large_price=10)
Traceback (most recent call last):
  ...
TypeError: profit() missing 1 required positional argument: 'small_cups'

5.4 · תיעוד, main וקונבנציות

docstring

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

>>> import math
>>> def num_of_bottles(total_liters):
...     """Calculates how many 1.5 L bottles to take.
...     :param total_liters: total water needed
...     :type total_liters: float
...     :return: the number of bottles
...     :rtype: int
...     """
...     return math.ceil(total_liters / 1.5)
...
>>> num_of_bottles(4)
3
שורה מה כותבים
ראשונה מה הפונקציה עושה, במשפט
:param שם: מה הפרמטר. שורה לכל פרמטר
:type שם: הטיפוס שלו
:return: מה חוזר. בלי return: None
:rtype: הטיפוס של מה שחוזר

(math.ceil מעגלת כלפי מעלה, כי שני בקבוקים וחצי הם שלושה בקבוקים. היא מאותה ספרייה של sqrt.)

לקרוא את התיעוד

>>> print(num_of_bottles.__doc__.splitlines()[0])
Calculates how many 1.5 L bottles to take.

help(num_of_bottles) מציג את כל התיעוד, ו-num_of_bottles.__doc__ מחזיר אותו כמחרוזת. שני קווים תחתונים מכל צד.

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

main

תוכנית שרק מגדירה פונקציות לא מדפיסה כלום: פייתון מגדירה אותן, ומסיימת. צריך לקרוא להן. בקורס עושים את זה מתוך פונקציה ראשית בשם main, במבנה הזה:

def welcome_hikers():
    print("Welcome to the trail!")


def remind_water():
    print("Don't forget to drink water.")


def main():
    welcome_hikers()
    remind_water()


if __name__ == "__main__":
    main()
  • main היא הראשונה שנקראת כשמריצים את הקובץ, והיא קוראת לכל השאר
  • שורת ה-if המוזרה נראית מסתורית עכשיו. היא תהיה הגיונית כשתכתבו ספריות משלכם. בינתיים: הקורס מבקש לכתוב לפי המבנה הזה מעכשיו
  • השם main הוא קונבנציה, לא חוק. ובכל זאת, main
  • שתי שורות ריקות בין פונקציות. כך ממליץ PEP 8, ולכן גם בדוגמה

קוד קריא

הסרטון מראה תוכנית שבה המשתנים נקראים c, t, ts, ואי אפשר להבין מה היא עושה. אותה תוכנית בדיוק, עם שמות ברורים ותיעוד, מתגלה כתחזית מזג אוויר לעיר שהמשתמש מקליד. ההבדל הוא רק בשמות.

ארבעה כללים

  1. מטרה אחת לכל פונקציה. פונקציה ממוקדת אפשר להשתמש בה בהרבה מקומות
  2. שם שאומר מה היא עושה, ותיעוד שתואם לו
  3. קלט ופלט ברורים
  4. שורה ריקה בין פונקציות (שתיים, לפי PEP 8)

בדיקה עצמית

  1. מה ההבדל בין print(x) לבין return x בתוך פונקציה?
  2. מה יודפס? def f(): x = 1 ואז f() ואז print(x).
  3. מה מחזירה פונקציה שאין בה return?
  4. האם מותר def g(a, b=2, c):?
  5. במה square שונה מ-square()?

תשובות. 1: print מציג ושוכח. return מחזיר ערך שאפשר לשמור ולהמשיך איתו. 2: NameError. x לוקאלי, וקיים רק בתוך f. 3: None. 4: לא. אחרי פרמטר עם ברירת מחדל, גם c חייב לקבל ברירת מחדל. 5: square הוא הפונקציה עצמה, כאובייקט. square() מריץ אותה.

יחידה 6 — רשימות

מפת היחידה

פרק דקה מה קורה שם
6.1 0:30 רשימה ריקה, ו-type
6.1 1:00 איברים מטיפוסים שונים, וגישה במיקום
6.1 1:39 רשימה בתוך רשימה
6.1 3:18 הפונקציה list
6.2 0:00 רשימה ומחרוזת הן רצפים
6.2 1:01 גישה וחיתוך
6.2 2:05 שרשור, ו-TypeError
6.2 3:11 הכפלה ו-len
6.3 0:31 רשימה ניתנת לשינוי
6.3 1:02 append ו-+=
6.3 1:33 שינוי איבר, ו-IndexError
6.3 3:11 remove, ו-ValueError
6.3 3:42 in
6.3 4:46 count ו-sort
6.3 5:16 sort מול sorted
6.3 6:22 מיון עם key

6.1 · מהי רשימה

רשימה (list) מחזיקה כמה אובייקטים במקום אחד, ושומרת על הסדר שלהם. סוגריים מרובעים, ופסיקים בין האיברים:

>>> movies = []
>>> type(movies)
<class 'list'>
>>> movies = ["Titanic", "Up", "Inception",
...           "Coco", "Amelie"]

האיברים לא חייבים להיות מאותו טיפוס:

>>> titanic = [1997, "Leonardo DiCaprio",
...            "Kate Winslet", 194, True]
>>> titanic[0]
1997
>>> titanic[1]
'Leonardo DiCaprio'
>>> titanic[3]
194

שנה, שני שחקנים, אורך בדקות, וצפיתי או לא. גישה היא לפי מיקום, מאפס, בדיוק כמו במחרוזת.

רשימה בתוך רשימה

איבר ברשימה יכול להיות רשימה. לאיבר הפנימי ניגשים עם זוג סוגריים נוסף:

>>> unique = [["a", "list"], ["of", "lists"]]
>>> unique[0]
['a', 'list']
>>> unique[0][1]
'list'
>>> unique[1][0]
'of'

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

list()

>>> empty_list = list()
>>> empty_list
[]
>>> list("Letters")
['L', 'e', 't', 't', 'e', 'r', 's']

list על מחרוזת מפרק אותה לתווים.

6.2 · רשימות ומחרוזות הן רצפים

רצף הוא אוסף של פריטים בסדר מסוים. מחרוזת היא רצף של תווים, ורשימה היא רצף של איברים. ולכן מה שלמדנו ביחידה 3 עובד גם כאן:

>>> my_subject = "chemistry"
>>> chemical_elements = ["hydrogen", "helium",
...                      "lithium", "beryllium"]
פעולה מחרוזת רשימה
גישה my_subject[3] הוא 'm' chemical_elements[3] הוא 'beryllium'
מהסוף my_subject[-1] הוא 'y' chemical_elements[-1] הוא 'beryllium'
חיתוך my_subject[1:3] הוא 'he' chemical_elements[1:3] הוא ['helium', 'lithium']
אורך len(my_subject) הוא 9 len(chemical_elements) הוא 4
>>> chemical_elements[1:3]
['helium', 'lithium']
>>> len(chemical_elements)
4

שרשור: רק רשימה לרשימה

>>> chemical_elements + "boron"
Traceback (most recent call last):
  ...
TypeError: can only concatenate list (not "str") to list
>>> chemical_elements + ["boron"]
['hydrogen', 'helium', 'lithium', 'beryllium', 'boron']

כמו שאי אפשר לשרשר מספר למחרוזת. העטיפה בסוגריים מרובעים הופכת את "boron" לרשימה עם איבר אחד.

הכפלה

>>> ["H", "O"] * 3
['H', 'O', 'H', 'O', 'H', 'O']

6.3 · פעולות על רשימות

ההבדל הגדול ממחרוזת

מחרוזת היא immutable. רשימה היא mutable: אפשר להוסיף, לשנות ולמחוק אחרי שהיא נוצרה.

>>> solar_system = ["Mercury", "Venus", "Earth",
...                 "Mars", "Jupiter", "Saturn",
...                 "Uranus", "Neptune"]
>>> len(solar_system)
8

להוסיף

>>> solar_system.append("Novo")
>>> solar_system[-1]
'Novo'
>>> solar_system += ["Novo"]
>>> solar_system[-2:]
['Novo', 'Novo']

שתי דרכים: append לאיבר אחד, ו-+= עם רשימה (שיכולה להכיל כמה איברים). שימו לב: append לא מחזירה כלום. היא משנה את הרשימה עצמה.

לשנות

>>> solar_system[8] = "De Novo"
>>> solar_system[9] = "Sun"
>>> solar_system[10] = "Sun"
Traceback (most recent call last):
  ...
IndexError: list assignment index out of range

משנים איבר במיקום שקיים. מיקום שלא קיים זו שגיאה, וכדי להוסיף בסוף משתמשים ב-append.

למחוק

>>> solar_system.remove("De Novo")
>>> solar_system.remove("De Novo")
Traceback (most recent call last):
  ...
ValueError: list.remove(x): x not in list
  • remove מקבלת ערך, לא מיקום, ומוחקת רק את המופע הראשון שלו
  • ערך שלא קיים ברשימה: ValueError

לכן בודקים לפני:

>>> "Earth" in solar_system
True
>>> "Pluto" in solar_system
False

עוד מתודות ופונקציות

(הטמפרטורות כאן לדוגמה בלבד.)

>>> temperature = [59, -180, 867, -180, 333]
>>> temperature.count(-180)
2
>>> max(temperature)
867
>>> sorted(temperature)
[-180, -180, 59, 333, 867]
>>> temperature
[59, -180, 867, -180, 333]
>>> temperature.sort()
>>> temperature
[-180, -180, 59, 333, 867]
מה מחזיר משנה את הרשימה?
sorted(lst) רשימה חדשה ממוינת לא
lst.sort() None כן

זה ההבדל ממה שהכרנו במחרוזות: "abc".upper() מחזירה מחרוזת חדשה ולא נוגעת במקורית. מתודות של רשימה כמו sort ו-append משנות את הרשימה עצמה.

מיון עם key

>>> astro_objects = ["moon", "asteroid", "galaxy"]
>>> sorted(astro_objects)
['asteroid', 'galaxy', 'moon']
>>> sorted(astro_objects, key=len)
['moon', 'galaxy', 'asteroid']

key הוא פרמטר אופציונלי שמקבל פונקציה. היא רצה על כל איבר, והמיון נעשה לפי מה שהיא החזירה. כאן len, ולכן לפי אורך. (שימו לב: len בלי סוגריים. מעבירים את הפונקציה עצמה, כמו ביחידה 5.)

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

בדיקה עצמית

  1. עם grid = [[1, 2], [3, 4]], מה זה grid[1][0]?
  2. מה ההבדל בין lst.append([5]) לבין lst += [5]?
  3. מה יחזיר x = [3, 1, 2].sort(), ועל מה מצביע x?
  4. למה "hi"[0] = "H" נכשל, ו-["h", "i"][0] = "H" לא?
  5. איך ממיינים רשימת מילים מהארוכה לקצרה? (רמז: help(sorted))

תשובות. 1: 3. 2: append([5]) מוסיף רשימה כאיבר אחד, [..., [5]]. += [5] מוסיף את 5. 3: None. sort משנה את הרשימה ולא מחזירה אותה. צריך sorted. 4: מחרוזת היא immutable, ורשימה היא mutable. 5: sorted(words, key=len, reverse=True).

יחידה 7 — לולאות

מפת היחידה

פרק דקה מה קורה שם
7.1 0:31 "כל עוד": הרעיון של while
7.1 1:05 איטרציה, ומתי הלולאה נעצרת
7.1 1:36 מונה מ-0 עד 4
7.1 2:10 לולאה אינסופית
7.1 2:40 הקלט שלא נגמר: שגיאה לוגית
7.1 4:49 break ו-continue
7.2 0:30 שלוש פעמים אותו קוד
7.2 1:32 for
7.2 2:34 המשתנה אחרי הלולאה
7.2 2:50 for על מחרוזת, ועל רשימה של רשימות
7.2 3:42 אותו דבר ב-while וב-for
7.2 4:14 range

7.1 · לולאת while

while חוזרת על בלוק כל עוד תנאי מתקיים. כמו דוב בתרדמת חורף: כל עוד מזג האוויר חורפי, הוא ממשיך לישון. מספר הסבבים לא ידוע מראש.

>>> i = 0
>>> while i < 5:
...     print(i)
...     i += 1
...
0
1
2
3
4
  • כל סבב נקרא איטרציה
  • לפני כל איטרציה פייתון בודקת את התנאי. False: הלולאה נגמרת. True: הבלוק רץ, וחוזרים לבדוק
  • i += 1 הוא קיצור של i = i + 1. בלעדיו i נשאר 0, והלולאה לא נגמרת

לולאה אינסופית

while True:
    handle_next_request()

לפעמים בכוונה: שרת שאמור לעבוד כל הזמן. הרבה יותר פעמים בטעות, בגלל תנאי שלא יכול להפוך ל-False. מפסיקים תוכנית תקועה עם Ctrl+C.

הקלט שלא נגמר

התוכנית שואלת כמה חודשים בשנה דובי גריזלי ישנים, וממשיכה לשאול עד שעונים 5, 6 או 7:

user_input = input("Hibernation months? ")
while (user_input != 5 and user_input != 6
       and user_input != 7):
    print(user_input)
    user_input = input("Try again: ")

עונים 7, ושוב נשאלים. עונים 5, ושוב. הלולאה לא נגמרת אף פעם, כי input מחזיר מחרוזת, ו-"7" אף פעם לא שווה ל-7. התיקון:

user_input = int(input("Hibernation months? "))
while (user_input != 5 and user_input != 6
       and user_input != 7):
    print(user_input)
    user_input = int(input("Try again: "))

זו שגיאה לוגית. אין הודעת שגיאה ואין קריסה. התוכנית פשוט עושה משהו אחר ממה שהתכוונתם. ולכן היא הכי קשה למצוא.

break ו-continue

מה עושה
break יוצא מהלולאה מיד, בלי עוד איטרציות
continue מדלג על שאר האיטרציה הנוכחית, וממשיך לבאה
>>> i = 0
>>> while i < 10:
...     i += 1
...     if i % 2 == 0:
...         break
...     print(i)
...
1

% הוא שארית חלוקה. i % 2 == 0 פירושו "i זוגי". הלולאה הדפיסה 1, ועצרה כש-i הגיע ל-2.

>>> i = 0
>>> while i < 10:
...     i += 1
...     if i % 2 == 0:
...         continue
...     print(i)
...
1
3
5
7
9

אותו קוד עם continue: במספר זוגי ה-print מדולג, והלולאה ממשיכה.

7.2 · לולאת for

הבעיה

animals = ["bear", "husky", "reindeer"]
animal = animals[0]
print(animal)
animal = animals[1]
print(animal)
animal = animals[2]
print(animal)

אותן שתי שורות שלוש פעמים, ורק המיקום משתנה. "עבור כל חיה ברשימה, הדפס אותה" כתוב בפייתון כמעט באותן מילים:

>>> animals = ["bear", "husky", "reindeer"]
>>> for animal in animals:
...     print(animal)
...
bear
husky
reindeer

for, שם משתנה, in, ורצף. in כאן הוא חלק מהלולאה, ולא הבדיקה מיחידה 2: בכל איטרציה הוא משים ל-animal את האיבר הבא.

המשתנה נשאר

>>> animal
'reindeer'

אחרי הלולאה המשתנה עדיין קיים, ומצביע על האיבר האחרון.

על כל רצף

מחרוזת היא רצף, ולכן for עובר עליה תו אחרי תו:

>>> for letter in "bear":
...     print(letter)
...
b
e
a
r

ורשימה של רשימות:

>>> animal_sounds = [["dog", "woof"],
...                  ["cat", "meow"],
...                  ["cow", "moo"]]
>>> for pair in animal_sounds:
...     print(pair[0], pair[1])
...
dog woof
cat meow
cow moo

אותו דבר, שלוש דרכים

>>> num = 0
>>> while num < 5:
...     print(num, end=" ")
...     num += 1
...
0 1 2 3 4 
>>> for num in [0, 1, 2, 3, 4]:
...     print(num, end=" ")
...
0 1 2 3 4 
>>> for num in range(5):
...     print(num, end=" ")
...
0 1 2 3 4 

(end=" " מדפיס רווח במקום שורה חדשה, כדי שהפלט ייכנס בשורה אחת.)

range

range מייצרת סדרת מספרים, בלי לכתוב רשימה:

פירוש מתקבל
range(5) מ-0 עד 5, בלי 5 0 1 2 3 4
range(5, 10) מ-5 עד 10 5 6 7 8 9
range(0, 10, 3) מ-0 עד 10, בקפיצות של 3 0 3 6 9
range(5, 0, -1) קפיצה שלילית 5 4 3 2 1
>>> list(range(0, 10, 3))
[0, 3, 6, 9]
>>> list(range(5, 0, -1))
[5, 4, 3, 2, 1]

range(start, stop, step): start ברירת מחדל 0, step ברירת מחדל 1, ו-stop לא נכלל. בדיוק כמו בחיתוך.

while או for

while for
מתי לא יודעים מראש כמה פעמים עוברים על רצף, או מספר פעמים ידוע
דוגמה עד שהמשתמש יענה נכון כל איבר ברשימה
סכנה לולאה אינסופית כמעט אין

בדיקה עצמית

  1. מה ידפיס for x in range(2, 8, 2): print(x)?
  2. למה while i < 5: print(i) (בלי שום שורה נוספת) לא נגמרת?
  3. מה ההבדל בין break ל-continue?
  4. אחרי for c in "abc": pass, על מה מצביע c?
  5. איך מדפיסים את המספרים מ-10 עד 1?

תשובות. 1: 2, 4, 6. 8 לא נכלל. 2: i לא משתנה, ולכן התנאי נשאר True. 3: break יוצא מהלולאה. continue מדלג רק על האיטרציה הנוכחית. 4: 'c', האיבר האחרון. 5: for n in range(10, 0, -1): print(n).

יחידה 8 — טאפל ומילון

מפת היחידה

פרק דקה מה קורה שם
8.1 0:33 טאפל ריק, וסוגריים עגולים
8.1 1:05 טאפל בלי סוגריים
8.1 1:36 טאפל עם איבר אחד: הפסיק
8.1 2:38 מה משותף לטאפל ולרשימה
8.1 4:25 טאפל לא ניתן לשינוי
8.1 5:26 למה בכלל טאפל
8.1 6:32 מתי טאפל ומתי רשימה
8.2 0:30 השמה לכמה משתנים בבת אחת
8.2 2:10 פונקציה שמחזירה כמה ערכים
8.2 4:15 תבנית מחרוזת עם %
8.2 6:17 %d, ולמה זה עדיף על +
8.3 0:32 מפתח וערך
8.3 1:02 {} ו-dict()
8.3 2:40 שליפה לפי מפתח
8.3 3:14 KeyError
8.3 4:16 הוספה ועדכון
8.3 6:05 del ו-pop
8.3 6:38 keys, values, items

8.1 · טאפל

טאפל (tuple) מחזיק כמה איברים, גם מטיפוסים שונים, בדיוק כמו רשימה. ההבדל: אחרי שנוצר, אי אפשר לשנות אותו.

>>> my_first_tuple = ()
>>> type(my_first_tuple)
<class 'tuple'>
>>> lunch = (3.5, 2, "sandwich")
>>> lunch = 3.5, 2, "sandwich"
>>> lunch
(3.5, 2, 'sandwich')

סוגריים עגולים, ופסיקים. ובלי סוגריים בכלל, פייתון אורזת את הערכים לטאפל לבד.

איבר אחד: הפסיק

>>> type((20))
<class 'int'>
>>> type((20,))
<class 'tuple'>

(20) הוא רק 20 בסוגריים, כמו בחשבון. הפסיק הוא מה שהופך את זה לטאפל.

מה עובד כמו ברשימה

טאפל הוא רצף, ולכן גישה, חיתוך, len, in ולולאת for עובדים:

>>> lunch[0]
3.5
>>> lunch[1:]
(2, 'sandwich')
>>> len(lunch)
3
>>> for item in lunch:
...     print(item)
...
3.5
2
sandwich

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

מה לא עובד

>>> salad = ("cucumber", "tomato", "tuna", "lemon")
>>> salad[2] = "spinach"
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment
רשימה טאפל מחרוזת
סוגריים [ ] ( ) מרכאות
שינוי mutable immutable immutable
הוספה ומחיקה כן לא לא

אז למה טאפל

  1. בטוח. מה שלא אמור להשתנות לא ישתנה בטעות
  2. חסכוני בזיכרון. לרשימה מוקצה מקום נוסף כדי שתוכל לגדול. לטאפל בדיוק כמה שצריך

מתי טאפל

כששני תנאים מתקיימים: מספר האיברים קבוע, ולמיקום יש משמעות. הדוגמה הקלאסית: נקודה בגרף. תמיד שני איברים, והראשון הוא x והשני y.

>>> point = (5, 2)

8.2 · שימושים בטאפל

השמה מרובה

>>> food, kind, calories = "bread", "whole wheat", 80
>>> food
'bread'
>>> calories
80

טאפל של שמות משמאל, טאפל של ערכים מימין, וכל שם מקבל את הערך שבמקום שלו. וזה מה שקרה בהחלפת הערכים ביחידה 2:

>>> my_basket, your_basket = "bread", "fruit"
>>> my_basket, your_basket = your_basket, my_basket
>>> my_basket
'fruit'

פונקציה שמחזירה כמה ערכים

ביחידה 5 ראינו return a, b. מה שחוזר הוא טאפל:

>>> def picnic_shopping(prices):
...     total = sum(prices)
...     return total, total <= 150
...
>>> result = picnic_shopping([12.5, 25, 60])
>>> type(result)
<class 'tuple'>
>>> money, is_budget_possible = picnic_shopping([12.5, 25, 60])
>>> money
97.5
>>> is_budget_possible
True

(sum מחברת את כל האיברים ברשימה.)

תבנית מחרוזת

שרשור עם + עובד, אבל ארוך ומבולגן, ונופל על מספרים:

>>> fruit = "grapes"
>>> cheese = "brie"
>>> print("In the basket: " + fruit + " and " + cheese)
In the basket: grapes and brie
>>> print("In the basket: %s and %s" % (fruit, cheese))
In the basket: grapes and brie
%s מקום לערך (s מ-string)
%d מקום למספר שלם
% (a, b) אחרי המחרוזת: טאפל של הערכים, לפי הסדר

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

>>> drivers = 1
>>> role = "designated driver"
>>> print("We need %d %s" % (drivers, role))
We need 1 designated driver
>>> print("We need " + drivers + " " + role)
Traceback (most recent call last):
  ...
TypeError: can only concatenate str (not "int") to str

אפשר גם לשים את הטאפל במשתנה מראש: basket_content = (drivers, role), ואז "..." % basket_content.

זו השיטה הישנה. היא נפוצה מאוד, ותראו אותה בהרבה קוד. השיטה החדשה היא המתודה format, והסרטון מפנה לקישור עליה.

8.3 · מילון

ברשימה ניגשים לערך לפי מיקום, מספר שלא אומר כלום. במילון (dict) ניגשים לפי מפתח (key), שיש לו משמעות. כל איבר הוא זוג: מפתח וערך (value).

>>> empty = {}
>>> type(empty)
<class 'dict'>
>>> empty = dict()
>>> keyboard_shortcuts = {"copy": "Ctrl+C",
...                       "paste": "Ctrl+V",
...                       "undo": "Ctrl+Z"}

סוגריים מסולסלים, נקודתיים בין מפתח לערך, ופסיקים בין זוגות.

סוגריים גישה לפי
רשימה [ ] מיקום
טאפל ( ) מיקום
מילון { } מפתח

שליפה

>>> keyboard_shortcuts["paste"]
'Ctrl+V'
>>> keyboard_shortcuts["cut"]
Traceback (most recent call last):
  ...
KeyError: 'cut'
  • שולפים לפי מפתח בלבד. אין שליפה הפוכה, מערך למפתח. גם במילון של מילים מחפשים את המילה, ולא את ההסבר
  • מפתח שלא קיים: KeyError. בודקים לפני עם in:
>>> "cut" in keyboard_shortcuts
False

הוספה ועדכון

מילון הוא mutable. אותה צורה מוסיפה ומעדכנת:

>>> keyboard_shortcuts["cut"] = "Ctrl+X"
>>> keyboard_shortcuts["paste"] = "Shift+Insert"
>>> keyboard_shortcuts["paste"]
'Shift+Insert'

כל מפתח ייחודי. השמה למפתח שכבר קיים לא יוצרת זוג נוסף ולא זורקת שגיאה. היא מחליפה את הערך.

מחיקה

>>> del keyboard_shortcuts["copy"]
>>> keyboard_shortcuts.pop("paste")
'Shift+Insert'
>>> keyboard_shortcuts
{'undo': 'Ctrl+Z', 'cut': 'Ctrl+X'}

del מוחק. pop מוחק ומחזיר את הערך.

לעבור על מילון

>>> keyboard_shortcuts.keys()
dict_keys(['undo', 'cut'])
>>> keyboard_shortcuts.values()
dict_values(['Ctrl+Z', 'Ctrl+X'])
>>> keyboard_shortcuts.items()
dict_items([('undo', 'Ctrl+Z'), ('cut', 'Ctrl+X')])
>>> for info in keyboard_shortcuts.items():
...     print("%s: %s" % info)
...
undo: Ctrl+Z
cut: Ctrl+X

items מחזיר כל זוג כטאפל, ולכן אפשר להעביר אותו ישר לתבנית המחרוזת. מה שחוזר מ-keys, values ו-items הוא view object: אפשר לעבור עליו בלולאה, והוא לא העתק של המילון.

בדיקה עצמית

  1. מה הטיפוס של ("a")? ושל ("a",)?
  2. מה ידפיס "%s is %d" % ("pi", 3)?
  3. מה קורה ב-d = {"a": 1} ואז d["a"] = 2?
  4. במה pop שונה מ-del?
  5. נקודה במרחב, (x, y, z): טאפל או רשימה? ורשימת קניות?

תשובות. 1: str, ו-tuple. 2: pi is 3. 3: הערך של "a" מתעדכן ל-2. מפתח ייחודי, אז אין זוג שני. 4: pop גם מחזיר את הערך שנמחק. 5: נקודה: טאפל, כי מספר האיברים קבוע ולמיקום יש משמעות. רשימת קניות: רשימה, כי מוסיפים ומוחקים ממנה.

טיפוסים והסבה

ארבעת הטיפוסים

טיפוס דוגמה הערה
str "Python", 'a' כל דבר במרכאות, גם "5"
int 2, -7 שלם
float 7.25, 7.0 עשרוני. 7.0 הוא float ולא int
bool True, False אות גדולה. כל מספר שאינו 0 נחשב True

בודקים טיפוס עם type():

>>> type(7.0)
<class 'float'>
>>> type("7")
<class 'str'>

הסבה

כותבים את הטיפוס הרצוי, ובסוגריים את הערך:

>>> int("5") ** 2
25
>>> float("5")
5.0
>>> str(3) + str(3)
'33'
>>> bool(0)
False

איפה זה תופס אתכם

input מחזיר תמיד מחרוזת. גם אם הוקלד 5, מתקבל "5". כל חישוב על קלט צריך הסבה קודם. בלי זה:

>>> "5" ** 2
Traceback (most recent call last):
  ...
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'

+ תלוי בטיפוס. על מספרים הוא חיבור, ועל מחרוזות שרשור. 3 + 3 הוא 6, ו-"3" + "3" הוא "33".

math.sqrt מחזירה float. math.sqrt(9) הוא 3.0.

לכתוב טוב

משתנה שהתחיל כמספר לא עובר להצביע על מחרוזת. זה אפשרי, וזה bad practice: פעולה שמתאימה לטיפוס אחד נשברת על השני.

בדיקה עצמית

  1. מה הטיפוס של 7.0? ושל "7.0"?
  2. מה יודפס: print(int("4") + 1)?
  3. למה input("age: ") + 1 נכשל?

תשובות. 1: float, ו-str. 2: 5. 3: input מחזיר מחרוזת, ואי אפשר לחבר מחרוזת ומספר. צריך int(input("age: ")) + 1.

ביטויים לוגיים

האופרטורים

אופרטור שואל x = 2
== שווה? x == 5 הוא False
!= שונה? x != 5 הוא True
<, >, <=, >= קטן, גדול x < 4 הוא True
not הופך not x < 4 הוא False
and גם וגם נכון רק אם שני הצדדים נכונים
or לפחות אחד נכון אם צד אחד מספיק
in נמצא בתוך? "break" in "breakfast" הוא True

ביטוי הוא ערך

תוצאה של ביטוי לוגי היא True או False, ואפשר לשים אותה במשתנה:

>>> number_of_sandwiches = 2
>>> i_am_hungry = number_of_sandwiches < 4
>>> i_am_hungry
True

and מול or

>>> cups_of_coffee = 3
>>> number_of_sandwiches < 4 and cups_of_coffee > 5
False
>>> number_of_sandwiches < 4 or cups_of_coffee > 5
True
צד שמאל צד ימין and or
True True True True
True False False True
False True False True
False False False False

איפה זה תופס אתכם

  • = מול ==. אחד משים, השני משווה.
  • true באות קטנה אינו ערך בוליאני. פייתון תחפש משתנה בשם הזה.
  • 0 הוא False, וכל מספר אחר הוא True. גם מספר שלילי.

לכתוב טוב

ביטוי של כמה תנאים: רווח משני צדי כל אופרטור, וכל תנאי בסוגריים.

>>> bowls_of_soup = 0
>>> (((number_of_sandwiches >= 1)
...   and (number_of_sandwiches < 4))
...  or (bowls_of_soup == 2))
True

בדיקה עצמית

  1. מה התוצאה של not (3 == 3)?
  2. מה התוצאה של "a" in "banana" and 5 > 7?
  3. מה התוצאה של bool(-1)?

תשובות. 1: False. 2: False, כי הצד הימני לא נכון. 3: True, כי כל מספר שאינו אפס נחשב אמת.

מרכאות ותווים מיוחדים

איזו מרכאה לבחור

המחרוזת מכילה עוטפים ב דוגמה
גרש כפולות "What's up"
מרכאות כפולות יחידות 'She said "maybe"'
גם וגם לוכסן הפוך לפני התו "She said \"don't\""
כמה שורות משולשות """..."""

הכלל שלא משתנה: המחרוזת נסגרת באותו סוג מרכאה שבו נפתחה.

התווים המיוחדים

כותבים מקבלים
\n שורה חדשה
\t טאב
\", \' מרכאה כחלק מהטקסט
\\ לוכסן הפוך אחד

הלוכסן ההפוך נקרא escape character: הוא מנטרל את המשמעות הרגילה של התו שבא אחריו.

יצירת מחרוזת מחלקים

>>> "We " + "belong " + "together"
'We belong together'
>>> "ha" * 3
'hahaha'
>>> str(3) + "!"
'3!'

איפה זה תופס אתכם

נתיב של קובץ ב-Windows. "C:\tournaments\new.txt" מכיל \t ו-\n, ופייתון תדפיס טאב ושורה חדשה. צריך \\ בכל מקום.

print מול הצגה באינטרפרטר. רק print מפרש את \n ו-\t. בלי print רואים אותם כמו שהם.

רווחים בשרשור. "We" + "belong" הוא "Webelong".

בדיקה עצמית

  1. איך כותבים את המחרוזת It's "fine" בפייתון?
  2. כמה שורות יודפסו ב-print("a\nb\nc")?
  3. מה יוצא מ-"-" * 5?

תשובות. 1: 'It\'s "fine"' או "It's \"fine\"", או במרכאות משולשות. 2: שלוש. 3: '-----'.

אינדקס וחיתוך

המפה

>>> word = "gaming"
תו g a m i n g
מההתחלה 0 1 2 3 4 5
מהסוף -6 -5 -4 -3 -2 -1

הצורות

כותבים מקבלים למה
word[0] 'g' התו הראשון
word[-1] 'g' התו האחרון
word[0:2] 'ga' מ-0 עד 2, בלי 2
word[2:5] 'min' שלושה תווים: 5 - 2
word[:2] 'ga' בלי התחלה: מ-0
word[2:] 'ming' בלי סוף: עד הסוף
word[:] 'gaming' הכל
word[:-1] 'gamin' הכל חוץ מהאחרון
word[::2] 'gmn' כל תו שני, מ-0
word[1::2] 'aig' כל תו שני, מ-1
>>> word[2:5]
'min'
>>> word[::2]
'gmn'

איפה זה תופס אתכם

הסוף לא נכלל. זו הטעות הנפוצה ביותר בחיתוך.

מתחילים מאפס. התו הראשון הוא [0], והאחרון הוא [אורך פחות 1], או פשוט [-1].

אינדקס מחוץ לגבול נותן שגיאה, וחיתוך מחוץ לגבול לא:

>>> word[10]
Traceback (most recent call last):
  ...
IndexError: string index out of range
>>> word[2:100]
'ming'

מחרוזת לא ניתנת לשינוי (immutable):

>>> word[0] = "G"
Traceback (most recent call last):
  ...
TypeError: 'str' object does not support item assignment
>>> word = "G" + word[1:]
>>> word
'Gaming'

לא רק מחרוזות

אותה גישה ואותו חיתוך עובדים על רשימה ועל טאפל, כי גם הם רצפים (יחידות 6 ו-8). ההבדל: ברשימה כן אפשר לשנות איבר.

>>> [10, 20, 30, 40][1:3]
[20, 30]
>>> (10, 20, 30)[-1]
30
>>> nums = [10, 20, 30]
>>> nums[0] = 99
>>> nums
[99, 20, 30]

בדיקה עצמית

עם s = "python":

  1. מה זה s[1:4]?
  2. מה זה s[-2:]?
  3. איך מקבלים "pto"?

תשובות. 1: 'yth'. 2: 'on'. 3: s[::2].

השגיאות שפגשנו

איך קוראים Traceback

  1. השורה האחרונה קודם. שם כתוב סוג השגיאה והסבר קצר
  2. מספר השורה אומר איפה פייתון נתקעה
  3. הסמן הקטן (^) מצביע על המקום המדויק בשורה

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

הטבלה

שגיאה מה היא אומרת איפה ראינו התיקון
SyntaxError הקוד לא בנוי לפי חוקי השפה 3.1: מרכאות. 5.3: ברירת מחדל לפני פרמטר בלי לתקן את הכתיבה
IndentationError הזחה במקום הלא נכון 4.1: רווחים בתחילת שורה ארבעה רווחים, רק בתוך בלוק
TypeError פעולה שהטיפוס לא תומך בה 2.3, 3.3, 5.1, 6.2, 8.1 הסבה, או טיפוס אחר
NameError שם שלא מוכר כאן 5.2: משתנה לוקאלי מחוץ לפונקציה לבדוק איות וטווח הכרה
IndexError מיקום שלא קיים 3.3, 6.3 לבדוק את len
ValueError הערך לא מתאים 6.3: remove של ערך שאינו ברשימה לבדוק עם in לפני
KeyError מפתח שלא קיים במילון 8.3 לבדוק עם in לפני
שגיאה לוגית אין הודעה. התוכנית עושה משהו אחר 7.1: לולאה שלא נגמרת לבדוק את ההנחות

TypeError: חמש פנים

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

>>> "5" ** 2
Traceback (most recent call last):
  ...
TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'
>>> "Change me"[0] = "S"
Traceback (most recent call last):
  ...
TypeError: 'str' object does not support item assignment
>>> ("a", "b")[0] = "c"
Traceback (most recent call last):
  ...
TypeError: 'tuple' object does not support item assignment
>>> ["H"] + "O"
Traceback (most recent call last):
  ...
TypeError: can only concatenate list (not "str") to list
>>> def square(number):
...     return number ** 2
...
>>> square()
Traceback (most recent call last):
  ...
TypeError: square() missing 1 required positional argument: 'number'

השאר

>>> 'I really hope defining this string will work"
Traceback (most recent call last):
  ...
SyntaxError: unterminated string literal (detected at line 1)
>>> "joker"[5]
Traceback (most recent call last):
  ...
IndexError: string index out of range
>>> ["Earth"].remove("Pluto")
Traceback (most recent call last):
  ...
ValueError: list.remove(x): x not in list
>>> {"copy": "Ctrl+C"}["cut"]
Traceback (most recent call last):
  ...
KeyError: 'cut'

בגרסאות ישנות ה-SyntaxError של מרכאה פתוחה מנוסח EOL while scanning string literal, וזה מה שמופיע בסרטון.

השגיאה השקטה

answer = input("How many months? ")
while answer != 7:
    answer = input("Try again: ")

התוכנית לא קורסת, ואין שום הודעה. היא פשוט שואלת לנצח, כי "7" אינו 7. שגיאה לוגית היא הקשה ביותר, כי אין מה לקרוא. מה שעוזר: להדפיס ערכים באמצע (print(answer, type(answer))) ולבדוק את ההנחות.

בדיקה עצמית

איזו שגיאה תתקבל?

  1. print("hello')
  2. [1, 2, 3][3]
  3. "3" + 3
  4. {"a": 1}["b"]
  5. if True: ושורה אחריה בלי הזחה
  6. def f(): y = 1, ואז print(y)

תשובות. 1: SyntaxError. 2: IndexError, כי המיקום האחרון הוא 2. 3: TypeError. 4: KeyError. 5: IndentationError. 6: NameError, כי y לוקאלי.

לכתוב טוב

הקוד ירוץ גם בלי רוב הכללים האלה. פייתון לא אוכפת אותם. הם קיימים בשביל מי שיקרא את הקוד, כולל אתם בעוד חודש. המסמך שמרכז אותם הוא PEP 8. היוצא מן הכלל הוא ההזחה: שם הכלל הוא חלק מהשפה.

שמות

כלל נכון לא
אותיות קטנות character Character
מילים מופרדות בקו תחתון nose_length noselength, noseLength
קבוע באותיות גדולות PI = 3.14 pi = 3.14
שם שאומר מה יש בו fairy_tale_file_with_extension file
פונקציה: מה היא עושה num_of_bottles f, calc
לא אותו שם בפנים ובחוץ land_animal, sea_animal animal פעמיים

קבוע הוא הבטחה, לא נעילה. פייתון תאפשר לשנות את PI.

רווחים והזחה

כלל נכון לא
משני צדי אופרטור x = a + 1 x=a+1
אחרי פסיק f(a, b, c) f(a,b,c)
פרמטר אחד f(a) f( a )
הזחה של בלוק ארבעה רווחים טאב, או ערבוב של שניהם
בין פונקציות שתי שורות ריקות שום רווח

ביטוי לוגי ארוך

כל תנאי בסוגריים, ורווחים סביב האופרטורים:

(((sandwiches >= 1) and (sandwiches < 4))
 or (bowls_of_soup == 2))

elif במקום קינון

שלוש אפשרויות כתובות כ-if, elif, elif, else באותה רמה, ולא if בתוך else בתוך else.

פונקציות

  1. מטרה אחת לכל פונקציה
  2. docstring מיד אחרי ה-def: מה היא עושה, :param:, :type:, :return:, :rtype:
  3. קלט ופלט ברורים. ערך נכנס כארגומנט ויוצא ב-return
  4. לא לגעת במשתנים גלובליים מתוך פונקציה, אלא אם הם קבועים. ובטח לא לשנות אותם עם global
def num_of_bottles(total_liters):
    """Calculates how many 1.5 liter bottles to take.

    :param total_liters: the total amount of water needed
    :type total_liters: float
    :return: the number of bottles
    :rtype: int
    """
    return math.ceil(total_liters / 1.5)

מבנה התוכנית

def main():
    # the program starts here
    pass


if __name__ == "__main__":
    main()

הקורס מבקש את המבנה הזה בכל תוכנית מיחידה 5 והלאה.

טיפוס קבוע למשתנה

משתנה שהתחיל כמספר נשאר מספר. לעבור ממנו למחרוזת זה אפשרי, וbad practice.

בדיקה עצמית

מה לא בסדר, לפי מה שהקורס מבקש?

  1. NumberOfStudents=30
  2. MAX_SCORE = 100, כשהערך משתנה בהמשך התוכנית
  3. print(a,b)
  4. פונקציה בשם do_stuff בלי docstring, שמדפיסה את התוצאה במקום להחזיר אותה
  5. שתי פונקציות צמודות, בלי שורה ריקה ביניהן

תשובות. 1: אותיות גדולות וחסרים רווחים, צריך number_of_students = 30. 2: אותיות גדולות שמורות לקבוע. 3: חסר רווח אחרי הפסיק. 4: שם שלא אומר כלום, אין תיעוד, ו-print במקום return מונע שימוש בתוצאה. 5: צריך שתי שורות ריקות.

פונקציות ומתודות מוכנות

פונקציה מול מתודה

פונקציה מתודה
צורה שם(פרמטרים) אובייקט.שם(פרמטרים)
דוגמה len(x), math.sqrt(9) "abc".upper(), lst.append(4)
שייכת ל עומדת בפני עצמה, או בתוך ספרייה טיפוס מסוים

הנקודה מטעה. ב-math.sqrt היא אומרת "מתוך הספרייה math". ב-"abc".upper() היא אומרת "המתודה של המחרוזת הזו".

פונקציות

מה עושה דוגמה תוצאה
print מדפיס print("a", 1) a 1
input קולט. מחזיר מחרוזת input("? ") מה שהוקלד
type הטיפוס type(2) <class 'int'>
int, float, str, bool הסבה int("5") 5
list, tuple, dict יצירה או הסבה list("ab") ['a', 'b']
len אורך len([1, 2]) 2
max, sum הגדול, הסכום sum([1, 2]) 3
sorted רשימה חדשה ממוינת sorted([2, 1]) [1, 2]
range סדרת מספרים list(range(3)) [0, 1, 2]
math.sqrt שורש. מחזיר float math.sqrt(9) 3.0
help, dir עזרה, רשימת מתודות dir(str) רשימה

מתודות

טיפוס מתודה מה עושה משנה את המקור?
מחרוזת upper() אותיות גדולות לא, מחזירה חדשה
מחרוזת zfill(n) אפסים משמאל לא, מחזירה חדשה
רשימה append(x) מוסיפה בסוף כן, מחזירה None
רשימה remove(x) מוחקת מופע ראשון כן
רשימה count(x) כמה פעמים לא
רשימה sort() ממיינת כן, מחזירה None
מילון pop(k) מוחקת ומחזירה ערך כן
מילון keys(), values(), items() מפתחות, ערכים, זוגות לא

העמודה האחרונה היא מה שבודקים במבחנים. מתודות של מחרוזת לא יכולות לשנות אותה, כי מחרוזת היא immutable. מתודות של רשימה ומילון כן, והן בדרך כלל מחזירות None.

>>> s = "hi"
>>> s.upper()
'HI'
>>> s
'hi'
>>> lst = [2, 1]
>>> print(lst.sort())
None
>>> lst
[1, 2]

פרמטרים אופציונליים

חלק מהפונקציות מקבלות פרמטרים שלא חייבים לשלוח:

>>> sorted(["moon", "asteroid", "galaxy"], key=len)
['moon', 'galaxy', 'asteroid']
>>> print("a", "b", end="!\n")
a b!

מה כל פונקציה מקבלת: help(sorted), או התיעוד של פייתון.

איפה מחפשים

  • help(): ממשק עזרה. רווח מדפדף, q יוצא
  • help(sorted): ישר לפונקציה, וגם לפונקציות שכתבתם, אם יש להן docstring
  • dir(list): כל המתודות של רשימה
  • אינטרנט: רק תיעוד של פייתון 3

בדיקה עצמית

  1. מה יחזיר "42".zfill(6)?
  2. מה יודפס: x = [3, 1], y = x.sort(), print(y)?
  3. מה ההבדל בין sorted(lst) לבין lst.sort()?
  4. מה התוצאה של len({"a": 1, "b": 2})?

תשובות. 1: '000042'. 2: None. 3: sorted מחזירה רשימה חדשה ולא נוגעת במקור. sort משנה את המקור ומחזירה None. 4: 2, מספר הזוגות.

תנאים ולולאות

שלוש הצורות

מה עושה מתי
if / elif / else בוחר בלוק אחד החלטה
while חוזר כל עוד תנאי נכון לא יודעים כמה פעמים
for חוזר פעם לכל איבר ברצף רשימה, מחרוזת, range

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

if

>>> grade = 85
>>> if grade >= 90:
...     print("A")
... elif grade >= 80:
...     print("B")
... else:
...     print("C")
...
B
  • הבדיקה לפי הסדר, ועוצרת בראשון שמתקיים. 85 >= 80 נבדק רק כי 85 >= 90 נכשל
  • elif ו-else לא חובה. else רק אחד, ותמיד אחרון

while

>>> count = 3
>>> while count > 0:
...     print(count)
...     count -= 1
...
3
2
1

משהו בתוך הלולאה חייב לשנות את התנאי. אחרת היא לא תיגמר.

for

>>> for letter in "abc":
...     print(letter)
...
a
b
c
>>> for i in range(1, 10, 4):
...     print(i)
...
1
5
9

range(start, stop, step), ו-stop לא נכלל. כמו בחיתוך.

break ו-continue

>>> for n in range(10):
...     if n == 3:
...         continue
...     if n == 6:
...         break
...     print(n, end=" ")
...
0 1 2 4 5 

continue דילג על 3. break עצר ב-6, ולכן 6 עד 9 לא הודפסו.

איפה זה תופס אתכם

  • input בתנאי. if input() == 5 לא יתקיים אף פעם. int(input())
  • = בתנאי. if x = 5: הוא SyntaxError. צריך ==
  • שכחתם לקדם את המונה ב-while: לולאה אינסופית. Ctrl+C עוצר
  • in של for אינו in של בדיקה. ב-for x in lst הוא משים. ב-if x in lst הוא שואל
  • משתנה שנוצר רק בתוך if לא קיים אם הבלוק לא רץ

בדיקה עצמית

  1. כמה פעמים ירוץ for i in range(3, 3):?
  2. מה ידפיס for c in "hey": print(c * 2)?
  3. מה חסר כאן: while x < 10: print(x)?
  4. איך מדפיסים רק את המספרים הזוגיים בין 0 ל-10, בשורה אחת של for?

תשובות. 1: אפס פעמים. ההתחלה כבר שווה לסוף. 2: hh, ee, yy. 3: שורה שמגדילה את x. 4: for n in range(0, 11, 2): print(n).

לכתוב פונקציות

השלד

>>> def greet(name, greeting="Hello"):
...     """Builds a greeting.
...
...     :param name: who to greet
...     :type name: str
...     :param greeting: the opening word
...     :type greeting: str
...     :return: the full greeting
...     :rtype: str
...     """
...     return greeting + ", " + name
...
>>> greet("Noa")
'Hello, Noa'
>>> greet("Noa", "Hi")
'Hi, Noa'
>>> greet(greeting="Hey", name="Noa")
'Hey, Noa'
חלק מה זה
def greet(...): הגדרה. לא מריצה כלום
name פרמטר חובה
greeting="Hello" פרמטר עם ברירת מחדל. רק אחרי פרמטרי החובה
"""...""" docstring. help(greet) מציג אותו
return מה שהפונקציה מחזירה
greet("Noa") קריאה. "Noa" הוא הארגומנט
greeting="Hey" ארגומנט לפי שם. הסדר לא משנה

print או return

print return
מה קורה מוצג על המסך חוזר למי שקרא
אפשר לשמור במשתנה לא כן
מה הפונקציה מחזירה None את הערך
>>> def show(x):
...     print(x * 2)
...
>>> def give(x):
...     return x * 2
...
>>> a = show(5)
10
>>> b = give(5)
>>> print(a, b)
None 10

כמעט תמיד רוצים return. פונקציה שמחזירה ערך אפשר לשלב בחישוב, בתנאי, או בפונקציה אחרת.

כמה ערכים

>>> def min_max(numbers):
...     return min(numbers), max(numbers)
...
>>> low, high = min_max([4, 9, 1])
>>> low, high
(1, 9)

חוזר טאפל, ופורקים אותו לכמה משתנים.

טווח הכרה

מוגדר מוכר
גלובלי מחוץ לפונקציות בכל מקום
לוקאלי בתוך פונקציה רק בתוכה
>>> x = "global"
>>> def f():
...     x = "local"
...     return x
...
>>> f()
'local'
>>> x
'global'

השמה בתוך פונקציה יוצרת משתנה לוקאלי חדש, גם אם יש גלובלי באותו שם. לוקאלי קודם לגלובלי. global x משנה את זה, וזה לא מומלץ.

פונקציה היא אובייקט

>>> double = give
>>> double(7)
14
>>> sorted(["ccc", "a", "bb"], key=len)
['a', 'bb', 'ccc']

שם בלי סוגריים הוא הפונקציה עצמה. כך מעבירים len ל-key.

איפה זה תופס אתכם

  • שכחתם סוגריים: greet לא מריץ כלום, ואין שגיאה
  • שכחתם ארגומנט: TypeError: ... missing 1 required positional argument
  • ברירת מחדל באמצע: def f(a=1, b): הוא SyntaxError
  • משתנה לוקאלי מבחוץ: NameError
  • print במקום return: הפונקציה מחזירה None, ומה שבא אחריה נשבר

בדיקה עצמית

  1. מה מחזירה def f(): pass?
  2. האם מותר def f(a, b=2, c=3):?
  3. עם greet מלמעלה: מה יחזיר greet("Dan", greeting="Yo")?
  4. def add(a, b): print(a + b), ואז total = add(1, 2) * 2. מה יקרה?

תשובות. 1: None. 2: כן. כל הפרמטרים עם ברירת המחדל בסוף. 3: 'Yo, Dan'. 4: יודפס 3, ואז TypeError, כי add מחזירה None, ואי אפשר להכפיל None.

מבני נתונים: איזה מתי

הטבלה

מחרוזת רשימה טאפל מילון
כתיבה "abc" [1, "a"] (1, "a") {"k": 1}
ריק "" [] או list() () או tuple() {} או dict()
מה בפנים תווים כל דבר כל דבר זוגות מפתח וערך
גישה s[0] l[0] t[0] d["k"]
חיתוך כן כן כן לא
len, in, for כן כן כן כן, על המפתחות
+ ו-* כן כן כן לא
שינוי לא כן לא כן
איבר אחד "a" [1] (1,) {"k": 1}

mutable או immutable

immutable: מחרוזת וטאפל. כל "שינוי" יוצר אובייקט חדש.

mutable: רשימה ומילון. אפשר להוסיף, לשנות ולמחוק במקום.

>>> s = "cat"
>>> s.upper()
'CAT'
>>> s
'cat'
>>> lst = ["c", "a", "t"]
>>> lst.append("s")
>>> lst
['c', 'a', 't', 's']

upper לא שינתה את s, והחזירה מחרוזת חדשה. append שינתה את lst, והחזירה None. זה ההבדל שהכי קל להתבלבל בו.

איך בוחרים

השאלה אם כן
זה טקסט? מחרוזת
מוסיפים ומוחקים לאורך התוכנית? רשימה
מספר איברים קבוע, ולמיקום יש משמעות? טאפל. נקודה (x, y), תאריך
מחפשים לפי שם ולא לפי מיקום? מילון. קיצורי מקלדת, ציונים לפי סטודנט

דוגמה: אותו מידע, ארבע צורות

>>> name = "Python"
>>> scores = [90, 75, 88]
>>> point = (5, 2)
>>> grades = {"math": 90, "history": 75}
>>> name[0], scores[0], point[0], grades["math"]
('P', 90, 5, 90)

הסבות ביניהם

>>> list("abc")
['a', 'b', 'c']
>>> tuple([1, 2])
(1, 2)
>>> list({"a": 1, "b": 2})
['a', 'b']
>>> list({"a": 1}.items())
[('a', 1)]

list על מילון מחזיר את המפתחות.

בדיקה עצמית

  1. איזה מבנה מתאים לרשימת המשימות של היום, שמוסיפים לה ומוחקים ממנה?
  2. איזה מבנה מתאים לשמירת צבע כשלושה מספרים, (255, 0, 0)?
  3. מה ההבדל בין [1] לבין (1)?
  4. מה יחזיר len({"a": 1, "b": 2}), ומה יחזיר len("ab")?

תשובות. 1: רשימה. 2: טאפל. מספר קבוע, ולכל מקום יש משמעות. 3: [1] הוא רשימה. (1) הוא פשוט 1. טאפל צריך פסיק, (1,). 4: שניהם 2.

דוגמאות

אלה לא התרגילים של הקורס. הן נכתבו כאן כדי לראות איך הנושאים מתחברים, ובכל אחת כתוב מאיזו יחידה מגיע כל חלק. נסו לפתור לבד קודם, ורק אחר כך לקרוא את הפתרון.

1. ממוצע וציון

המשימה: פונקציה שמקבלת רשימת ציונים, ומחזירה את הממוצע ואת הציון במילה.

יחידות: 4, 5, 6.

>>> def grade_report(grades):
...     """Returns the average and a word for it.
...
...     :param grades: the grades
...     :type grades: list
...     :return: the average and the word
...     :rtype: tuple
...     """
...     average = sum(grades) / len(grades)
...     if average >= 90:
...         word = "excellent"
...     elif average >= 70:
...         word = "good"
...     else:
...         word = "needs work"
...     return average, word
...
>>> grade_report([95, 80, 72])
(82.33333333333333, 'good')

מה לשים לב: word מקבל ערך בכל אחד משלושת הענפים. אם היה חסר else, ממוצע נמוך היה משאיר את word בלי ערך, ו-return היה נותן UnboundLocalError, סוג של NameError. זו המלכודת שפרק 4.2 מזכיר.

2. ספירת מילים

המשימה: כמה פעמים כל מילה מופיעה ברשימה.

יחידות: 7, 8.

>>> def count_words(words):
...     counts = {}
...     for word in words:
...         if word in counts:
...             counts[word] += 1
...         else:
...             counts[word] = 1
...     return counts
...
>>> count_words(["to", "be", "or", "not", "to", "be"])
{'to': 2, 'be': 2, 'or': 1, 'not': 1}

מה לשים לב: בלי הבדיקה if word in counts, הפעם הראשונה של כל מילה הייתה נותנת KeyError, כי אין עוד מה להגדיל.

3. ניחוש מספר

המשימה: לבקש מהמשתמש לנחש עד שיצליח, ולספור ניסיונות.

יחידות: 2, 4, 5, 7.

SECRET = 7


def main():
    attempts = 0
    while True:
        guess = int(input("Guess a number: "))
        attempts += 1
        if guess == SECRET:
            print("Got it in %d tries" % attempts)
            break
        elif guess < SECRET:
            print("Higher")
        else:
            print("Lower")


if __name__ == "__main__":
    main()

מה לשים לב:

  • int(...) סביב input. בלעדיו זו בדיוק השגיאה הלוגית מפרק 7.1
  • SECRET באותיות גדולות, כי זה קבוע. ומותר לקרוא אותו מתוך הפונקציה, כי זה קבוע (פרק 5.2)
  • while True עם break כשיש יציאה ברורה מבפנים

4. מילה הפוכה ופלינדרום

המשימה: האם מילה זהה כשקוראים אותה מהסוף.

יחידות: 3, 5.

>>> def is_palindrome(word):
...     word = word.lower()
...     return word == word[::-1]
...
>>> is_palindrome("Level")
True
>>> is_palindrome("Python")
False

מה לשים לב: return word == word[::-1] מחזיר את תוצאת ההשוואה ישירות. אין צורך ב-if ...: return True else: return False. ו-lower, אחות של upper, כדי ש-L ו-l ייחשבו שווים.

5. מלאי

המשימה: מילון של מוצרים וכמויות. להדפיס את מה שנגמר.

יחידות: 7, 8.

>>> stock = {"apples": 12, "bread": 0,
...          "milk": 3, "eggs": 0}
>>> for item, amount in stock.items():
...     if amount == 0:
...         print("Out of %s" % item)
...
Out of bread
Out of eggs

מה לשים לב: for item, amount in stock.items() פורק כל זוג לשני משתנים, אותו פירוק כמו ב-a, b = b, a. הסרטון עובר על items עם משתנה אחד, ושני הסגנונות עובדים.

6. ממיר טמפרטורות

המשימה: רשימה של מעלות צלזיוס, ורשימה חדשה בפרנהייט.

יחידות: 5, 6, 7.

>>> def to_fahrenheit(celsius_list):
...     result = []
...     for c in celsius_list:
...         result.append(c * 9 / 5 + 32)
...     return result
...
>>> to_fahrenheit([0, 100, 37])
[32.0, 212.0, 98.6]

מה לשים לב: result = [] לפני הלולאה. אם היה בתוכה, הרשימה הייתה מתאפסת בכל איטרציה. ו-return result מחוץ ללולאה, אחרת הפונקציה הייתה חוזרת אחרי האיבר הראשון.

דף עזר

טיפוסים

דוגמה הסבה שינוי
str "Python" str(3) הוא '3' לא
int 2 int("5") הוא 5
float 7.25 float("5") הוא 5.0
bool True bool(0) הוא False
list [1, "a"] list("ab") כן
tuple (1, "a"), (1,) tuple([1, 2]) לא
dict {"k": 1} dict() כן

אופרטורים

+ - * / ** חשבון. ** חזקה
% שארית. n % 2 == 0 פירושו זוגי
+=, -= x += 1 הוא x = x + 1
+ על רצפים שרשור, רק מאותו טיפוס
* על רצף ומספר הכפלה: "ab" * 2 הוא 'abab'
== != < > <= >= השוואה
not and or לוגיקה
in הכלה: "b" in "abc", 3 in [1, 3], "k" in d

רצפים: מחרוזת, רשימה, טאפל

s[0], s[-1] ראשון, אחרון
s[a:b] מ-a עד b, בלי b
s[:b], s[a:], s[:] מההתחלה, עד הסוף, הכל
s[a:b:k] בקפיצות של k. s[::-1] הפוך
len(s) אורך
s.upper(), s.zfill(n) מחרוזת: גדולות, אפסים משמאל
\n \t \\ \" שורה, טאב, לוכסן, מרכאה
"%s is %d" % (a, b) תבנית מחרוזת

רשימה

lst.append(x) מוסיף בסוף. מחזיר None
lst += [x, y] מוסיף כמה
lst[i] = x מחליף. רק במיקום קיים
lst.remove(x) מוחק מופע ראשון. לא קיים: ValueError
lst.count(x) כמה פעמים
lst.sort() ממיין את הרשימה עצמה
sorted(lst, key=len) רשימה חדשה ממוינת
max(lst), sum(lst) הגדול, הסכום

מילון

d["k"] ערך לפי מפתח. לא קיים: KeyError
d["k"] = v מוסיף, או מעדכן אם קיים
del d["k"] מוחק
d.pop("k") מוחק ומחזיר את הערך
d.keys(), d.values(), d.items() מפתחות, ערכים, זוגות כטאפלים

תנאים ולולאות

if / elif / else הראשון שמתקיים, ורק הוא
while תנאי: כל עוד
for x in רצף: פעם לכל איבר
range(start, stop, step) סדרה. stop לא נכלל
break יוצא מהלולאה
continue מדלג לאיטרציה הבאה
pass בלוק ריק

פונקציות

def f(a, b=2): הגדרה. ברירות מחדל בסוף
return x, y מחזיר. כמה ערכים חוזרים כטאפל
בלי return מחזיר None
f(b=5, a=1) ארגומנטים לפי שם
"""...""" docstring: :param: :type: :return: :rtype:
global x משנה משתנה גלובלי. לא מומלץ
if __name__ == "__main__": main() מבנה התוכנית

פקודות

print(a, b, end=" ") הדפסה
input("...") קלט. תמיד מחרוזת
type(x) הטיפוס
import math, math.sqrt(x) שורש, float
help(f), f.__doc__ תיעוד. q יוצא
dir(str) המתודות
a, b = b, a החלפה

שגיאות

SyntaxError כתיבה לא תקינה
IndentationError הזחה לא במקום
TypeError טיפוס לא מתאים לפעולה
NameError שם לא מוכר, או מחוץ לטווח
IndexError מיקום לא קיים
ValueError ערך לא מתאים, למשל remove
KeyError מפתח לא קיים

PEP 8

snake_case משתנים ופונקציות
UPPER_CASE קבועים
ארבעה רווחים הזחה
x = a + b, f(a, b) רווחים
שתי שורות ריקות בין פונקציות

מילון מונחים

עברית אנגלית פירוש
משתנה variable שם שמצביע על אובייקט
השמה assignment =. קישור שם לערך
טיפוס type סוג האובייקט: int, str ועוד
מחרוזת string רצף תווים
בוליאני boolean True או False
הסבה casting המרה בין טיפוסים
קבוע constant ערך שלא משתנה. בקונבנציה, אותיות גדולות
קונבנציה convention מוסכמה. לא נאכפת על ידי השפה
תו בריחה escape character \. מנטרל את המשמעות של התו הבא
אינדקס index מיקום ברצף, מאפס
חיתוך slicing [a:b]
רצף sequence מחרוזת, רשימה, טאפל
ניתן לשינוי mutable רשימה, מילון
לא ניתן לשינוי immutable מחרוזת, טאפל
בלוק block שורות מוזחות שמתחת לשורה עם נקודתיים
הזחה indentation הרווחים בתחילת השורה
בקרת זרימה flow control if, לולאות, break
איטרציה iteration סבב אחד של לולאה
פונקציה function בלוק עם שם, שמבצע משימה
מתודה method פונקציה של טיפוס: x.upper()
פרמטר parameter השם בהגדרה
ארגומנט argument הערך בקריאה
ערך החזרה return value מה ש-return מחזיר
ברירת מחדל default ערך לפרמטר שלא נשלח
טווח הכרה scope איפה משתנה קיים
גלובלי, לוקאלי global, local מחוץ לפונקציה, בתוכה
תיעוד docstring ההסבר שבתוך הפונקציה
טאפל tuple רצף שלא ניתן לשינוי
מילון dictionary זוגות של מפתח וערך
שגיאה לוגית logic error בלי הודעה. התוכנית עושה משהו אחר
אינטרפרטר interpreter מקום להריץ שורה אחרי שורה

סיכום הקורס

יחידה אחרי יחידה

יחידה נושא מה אפשר לעשות אחריה
2 משתנים לשמור ערכים, לחשב, לקלוט מהמשתמש
3 מחרוזות לבנות טקסט, לחתוך אותו, להשתמש במתודות
4 תנאים להחליט מה ירוץ
5 פונקציות לכתוב קוד פעם אחת ולהשתמש בו שוב
6 רשימות להחזיק הרבה ערכים ולשנות אותם
7 לולאות לחזור על פעולה בלי לכתוב אותה שוב
8 טאפל ומילון לבחור את מבנה הנתונים שמתאים למידע

יחידות 0 ו-1 הן פתיחה והיכרות עם הסביבה, ולא נכנסו לכאן. יחידות 9 (קבצים) ו-10 (פרויקט מסכם) עוד לא סוכמו.

שלושה רעיונות שחוזרים

1. משתנה מצביע, הוא לא מכיל

יחידה 2 פותחת בזה: course = "Python" יוצר אובייקט, ו-course מצביע עליו. כל השאר נגזר מזה:

  • "לשנות" מחרוזת זה להצביע על מחרוזת חדשה (3.3)
  • פונקציה היא אובייקט, ושם בלי סוגריים מצביע עליה (5.1)
  • a, b = b, a רק מחליף הצבעות (2.3, 8.2)

2. מה משתנה ומה לא

לא ניתן לשינוי ניתן לשינוי
מחרוזת, טאפל רשימה, מילון
מתודה מחזירה חדש: s.upper() מתודה משנה במקום: lst.sort()

השאלה "האם זה משנה את המקור, או מחזיר חדש?" עונה על חלק גדול מהבלבולים בקורס.

3. input מחזיר מחרוזת

זה מופיע ביחידה 2, ושוב ב-4, ושוב ב-7, כל פעם בצורה אחרת: TypeError בחישוב, תנאי שלא מתקיים, ולולאה שלא נגמרת. בכל פעם שיש input ומספר, צריך int.

הרגלים שהקורס מבקש

  • שמות snake_case שאומרים מה יש בהם
  • ארבעה רווחים להזחה
  • docstring לכל פונקציה
  • main ו-if __name__ == "__main__" בכל תוכנית
  • לבדוק עם in לפני remove ולפני שליפה ממילון

לחזרה לפני מבחן

  1. השגיאות שפגשנו: לזהות שגיאה מההודעה
  2. מבני נתונים: הטבלה שמשווה את ארבעתם
  3. לכתוב פונקציות: print מול return, וטווח הכרה
  4. דוגמאות: לפתור לבד, ואז להשוות
  5. דף עזר: הכל בעמוד אחד