מבוא ל-Python

הקדמה

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

אם אתם מחפשים קורס MOOC משלים, כדאי לנסות את הקורסים של Udacity ו-Coursera (מבוא לתכנות [למתחילים] או מבוא ל-Python). לבסוף, אם אתם מחפשים למידה עצמית באינטרנט בלי לצפות בסרטונים, כדאי לנסות את האפשרויות שמפורטות לקראת סוף הפוסט הזה. כל אחת מהן כוללת תוכן למידה וגם מתורגמן אינטראקטיבי של Python שאפשר להתאמן איתו. מה זה 'מתורגמן' שהזכרנו? בקטע הבא נסביר איך עושים את זה.

מבוא לשפה

‫Python היא שפה דינמית ומפורשת (bytecode-compiled). אין הצהרות על סוגים של משתנים, פרמטרים, פונקציות או שיטות בקוד המקור. כך הקוד קצר וגמיש, אבל לא מתבצעת בדיקת סוגים בזמן ההידור של קוד המקור. ‫Python עוקבת אחרי הסוגים של כל הערכים בזמן הריצה ומסמנת קוד שלא הגיוני בזמן שהוא פועל.

דרך מצוינת לראות איך קוד Python עובד היא להריץ את רכיב התרגום של Python ולהקליד קוד ישירות לתוכו. אם יש לכם שאלה כמו "מה יקרה אם אוסיף int ל-list?", הדרך הכי מהירה וטובה לגלות את התשובה היא פשוט להקליד אותה במפרש של Python. (בהמשך מוסבר מה קורה בפועל!)

$ python3        ## Run the Python interpreter
Python 3.X.X (XXX, XXX XX XXXX, XX:XX:XX) [XXX] on XXX
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 6       ## set a variable in this interpreter session
>>> a           ## entering an expression prints its value
6
>>> a + 2
8
>>> a = 'hi'    ## 'a' can hold a string just as well
>>> a
'hi'
>>> len(a)      ## call the len() function on a string
2
>>> a + len(a)  ## try something that doesn't work
Traceback (most recent call last):
  File "", line 1, in 
TypeError: can only concatenate str (not "int") to str
>>> a + str(len(a))  ## probably what you really wanted
'hi2'
>>> foo         ## try something else that doesn't work
Traceback (most recent call last):
  File "", line 1, in 
NameError: name 'foo' is not defined
>>> ^D          ## type CTRL-d to exit (CTRL-z in Windows/DOS terminal)

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

כפי שאפשר לראות למעלה, קל להתנסות במשתנים ובאופרטורים. בנוסף, המפרש מעלה שגיאת זמן ריצה אם הקוד מנסה לקרוא משתנה שלא הוקצה לו ערך. בדומה ל-C++‎ ול-Java,‏ Python היא שפה תלוית-רישיות, ולכן 'a' ו-'A' הן משתנים שונים. סוף השורה מציין את סוף ההצהרה, ולכן בניגוד ל-C++‎ ול-Java, ב-Python לא צריך להוסיף נקודה-פסיק בסוף כל הצהרה. ההערות מתחילות בסימן '#' ונמשכות עד סוף השורה.

קוד מקור של Python

קבצי מקור של Python משתמשים בסיומת ‎.py ונקראים 'מודולים'. עם מודול Python‏ hello.py, הדרך הכי קלה להריץ אותו היא באמצעות פקודת המעטפת 'python hello.py Alice', שמפעילה את מתורגמן Python כדי להריץ את הקוד ב-hello.py, ומעבירה לו את הארגומנט של שורת הפקודה 'Alice'. בדף המסמכים הרשמי מפורטות כל האפשרויות השונות שזמינות כשמריצים Python משורת הפקודה.

הנה תוכנית hello.py פשוטה מאוד (שימו לב שבלוקים של קוד מוגבלים באמצעות הזחה ולא באמצעות סוגריים מסולסלים – נרחיב על כך בהמשך):

#!/usr/bin/python3

# import modules used here -- sys is a very standard one
import sys

# Gather our code in a main() function
def main():
    print('Hello there', sys.argv[1])
    # Command line args are in sys.argv[1], sys.argv[2] ...
    # sys.argv[0] is the script name itself and can be ignored

# Standard boilerplate to call the main() function to begin
# the program.
if __name__ == '__main__':
    main()

כך נראית הרצת התוכנית הזו משורת הפקודה:

$ python3 hello.py Guido
Hello there Guido
$ ./hello.py Alice  ## without needing 'python3' first (Unix)
Hello there Alice

ייבוא, ארגומנטים בשורת הפקודה ו-len()

ההצהרות החיצוניות ביותר בקובץ Python, או 'מודול', מבצעות את ההגדרה החד-פעמית שלו – ההצהרות האלה מופעלות מלמעלה למטה בפעם הראשונה שהמודול מיובא למקום כלשהו, ומגדירות את המשתנים והפונקציות שלו. אפשר להריץ מודול Python ישירות – כמו בדוגמה שלמעלה python3 hello.py Bob – או לייבא אותו ולהשתמש בו במודול אחר. כשמריצים קובץ Python ישירות, המשתנה המיוחד ‎__name__ מוגדר כ-‎__main__. לכן, נפוץ להשתמש בקוד הסטנדרטי if __name__ ==... שמוצג למעלה כדי להפעיל פונקציה main()‎ כשמריצים את המודול ישירות, אבל לא כשמודול אחר מייבא את המודול.

בתוכנית Python רגילה, הרשימה sys.argv מכילה את הארגומנטים של שורת הפקודה בדרך הרגילה, כאשר sys.argv[0] הוא התוכנית עצמה, sys.argv[1] הוא הארגומנט הראשון וכן הלאה. אם אתם יודעים מהו argv או מספר הארגומנטים, אתם יכולים פשוט לבקש את הערך הזה מ-Python באמצעות len(sys.argv), כמו שעשינו בקוד של רכיב התרגום האינטראקטיבי שלמעלה כשביקשנו את אורך המחרוזת. באופן כללי, הפונקציה len() יכולה להגיד לכם מה האורך של מחרוזת, כמה רכיבים יש ברשימות ובטפלים (מבנה נתונים נוסף שדומה למערך) וכמה זוגות של מפתח-ערך יש במילון.

פונקציות בהגדרת המשתמש

הגדרת פונקציות ב-Python:

# Defines a "repeat" function that takes 2 arguments.
def repeat(s, exclaim):
    """
    Returns the string 's' repeated 3 times.
    If exclaim is true, add exclamation marks.
    """

    result = s + s + s # can also use "s * 3" which is faster (Why?)
    if exclaim:
        result = result + '!!!'
    return result

שימו לב גם איך השורות שמרכיבות את הפונקציה או את משפט ה-if מקובצות יחד, כי לכולן יש אותה רמת הזחה. הצגנו גם 2 דרכים שונות לחזרה על מחרוזות, באמצעות האופרטור + שהוא יותר ידידותי למשתמש, אבל גם האופרטור * פועל כי הוא האופרטור 'חזרה' של Python, כלומר '-' * 10 נותן '----------', דרך נוחה ליצור 'קו' על המסך. בהערה בקוד רמזנו ש-‎ * ‎ פועל מהר יותר מ-‎+‎. הסיבה לכך היא ש-‎ * ‎ מחשב את הגודל של האובייקט שמתקבל פעם אחת, בעוד ש-‎+‎ מבצע את החישוב הזה בכל פעם שקוראים לו. הסימנים + ו- * נקראים אופרטורים 'עמוסים מדי' כי המשמעות שלהם שונה כשמדובר במספרים לעומת מחרוזות (וסוגי נתונים אחרים).

מילת המפתח def מגדירה את הפונקציה עם הפרמטרים שלה בתוך סוגריים והקוד שלה עם הזחה. השורה הראשונה של פונקציה יכולה להיות מחרוזת תיעוד (docstring) שמתארת את הפעולה של הפונקציה. המחרוזת יכולה להיות שורה אחת, או תיאור רב-שורה כמו בדוגמה שלמעלה. (כן, אלה 'מרכאות משולשות', תכונה ייחודית ל-Python!) משתנים שמוגדרים בפונקציה הם מקומיים לפונקציה הזו, ולכן התוצאה בפונקציה שלמעלה נפרדת ממשתנה התוצאה בפונקציה אחרת. ההצהרה return יכולה לקבל ארגומנט, ובמקרה כזה זה הערך שמוחזר למתקשר.

בדוגמה הבאה מוצג קוד שקורא לפונקציה repeat() שלמעלה ומדפיס את מה שהיא מחזירה:

def main():
    print(repeat('Yay', False))      ## YayYayYay
    print(repeat('Woo Hoo', True))   ## Woo HooWoo HooWoo Hoo!!!

בזמן הריצה, הפונקציות צריכות להיות מוגדרות על ידי ביצוע של def לפני שהן נקראות. בדרך כלל מגדירים פונקציה main() בחלק התחתון של הקובץ, ופונקציות שהיא קוראת להן מופיעות מעליה.

כניסה מהשוליים

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

בהתחלה, השימוש ברווחים הלבנים ב-Python נראה קצת מוזר, אבל הוא הגיוני והתרגלתי אליו מהר מאוד. מומלץ להימנע משימוש ב-TAB, כי הוא מסבך מאוד את תוכנית הכניסות (וגם כי המשמעות של TAB עשויה להיות שונה בפלטפורמות שונות). מגדירים את העורך כך שיכניס רווחים במקום מקשי Tab לקוד Python.

שאלה נפוצה בקרב מתחילים היא: "כמה רווחים צריך להוסיף לכניסה לתג?" לפי מדריך הסגנון הרשמי של Python‏ (PEP 8), צריך להשתמש ב-4 רווחים להזחה. (עובדה מעניינת: הנחיות הסגנון הפנימיות של Google קובעות שצריך להוסיף 2 רווחים לכניסה!)

הקוד נבדק בזמן הריצה

ב-Python מתבצעות מעט מאוד בדיקות בזמן ההידור, וכמעט כל הבדיקות של סוגים, שמות וכו' בכל שורה נדחות עד להרצת השורה. נניח שהפונקציה main() שלמעלה קוראת לפונקציה repeat() כך:

def main():
    if name == 'Guido':
        print(repeeeet(name) + '!!!')
    else:
        print(repeat(name))

המשפט if מכיל שגיאה ברורה, שבה הפונקציה repeat()‎ מוקלדת בטעות כ-repeeeet()‎. הדבר המצחיק ב-Python הוא שהקוד הזה עובר קומפילציה ופועל בצורה תקינה כל עוד השם בזמן הריצה הוא לא Guido. רק כשניסיון הרצה ינסה להפעיל את הפונקציה repeeeet(), הוא יזהה שאין פונקציה כזו ויציג שגיאה. יש גם שגיאה שנייה בקטע הקוד הזה: לא הוקצה ערך למשתנה name לפני ההשוואה שלו ל-'Guido'. אם תנסו להעריך משתנה שלא הוקצה לו ערך, Python תציג את השגיאה NameError. אלה כמה דוגמאות שממחישות שכאשר מריצים תוכנית Python בפעם הראשונה, חלק מהשגיאות הראשונות שרואים הן שגיאות הקלדה פשוטות או משתנים לא מאותחלים, כמו אלה. זהו תחום אחד שבו לשפות עם מערכת סוגים מפורטת יותר, כמו Java, יש יתרון… הן יכולות לזהות שגיאות כאלה בזמן ההידור (אבל כמובן שצריך לתחזק את כל מידע הסוגים הזה… זהו פשרה).

ב-Python 3 הוצגו רמזים לסוגים. הערות לגבי סוג מאפשרות לציין את הסוג של כל ארגומנט בפונקציה, וגם את הסוג של האובייקט שמוחזר על ידי הפונקציה. לדוגמה, בפונקציה עם ההערות def is_positive(n: int) -> bool:, הארגומנט n הוא int והערך המוחזר הוא bool. בהמשך נסביר מה המשמעות של הסוגים האלה. עם זאת, לא חובה להשתמש ברמזים לגבי סוגים. יותר ויותר קודים משתמשים ברמזים לגבי סוגים, כי אם משתמשים בהם, חלק מהעורכים כמו cider-v ו-VS.code יכולים להריץ בדיקות כדי לוודא שהפונקציות נקראות עם סוגי הארגומנטים הנכונים. הם יכולים אפילו להציע טיעונים ולאמת אותם בזמן שאתם עורכים את הקוד. במדריך הזה לא נסביר על רמזים לטיפוס, אבל חשוב לנו שתכירו אותם אם תשמעו עליהם או תראו אותם בשימוש.

שמות משתנים

מכיוון שמשתני Python לא כוללים סוג שמאוית בקוד המקור, כדאי לתת למשתנים שמות משמעותיים כדי לזכור מה קורה. לכן, משתמשים ב-name אם מדובר בשם יחיד, ב-names אם מדובר ברשימת שמות וב-tuples אם מדובר ברשימת טאפלים. הרבה שגיאות בסיסיות ב-Python נובעות מכך ששוכחים איזה סוג ערך יש בכל משתנה, לכן כדאי להשתמש בשמות המשתנים (זה כל מה שיש לכם) כדי לשמור על סדר.

בנוגע לשמות בפועל, בשפות מסוימות מקובל להשתמש בקו תחתון_בין_מילים בשמות של משתנים שמורכבים מ "יותר ממילה אחת", אבל בשפות אחרות מקובל להשתמש ב-CamelCase. באופן כללי, ב-Python מעדיפים את שיטת הקו התחתון, אבל מומלץ למפתחים להשתמש בשיטת CamelCase אם משלבים את הקוד עם קוד Python קיים שכבר משתמש בסגנון הזה. ציון הקריאוּת חשוב. מידע נוסף זמין בקטע על מוסכמות למתן שמות ב-PEP 8.

כפי שניתן לנחש, אי אפשר להשתמש במילות מפתח כמו if ו-while כשמות של משתנים – אם תעשו את זה, תקבלו שגיאת תחביר. עם זאת, חשוב להיזהר ולא להשתמש בפונקציות מובנות כשמות של משתנים. לדוגמה, השמות str,‏ list ו-print אולי נראים טובים, אבל הם יחליפו את משתני המערכת האלה. הפונקציות המובנות הן לא מילות מפתח, ולכן מפתחי Python חדשים עלולים להשתמש בהן בטעות.

מידע נוסף על מודולים ומרחבי השמות שלהם

נניח שיש לכם מודול בשם binky.py שמכיל פונקציה בשם foo(). השם המלא של הפונקציה foo הוא binky.foo. כך, מודולים שונים של Python יכולים לתת לפונקציות ולמשתנים שלהם כל שם שרוצים, ושמות המשתנים לא יתנגשו – module1.foo שונה מ-module2.foo. במונחים של Python, אפשר לומר שלכל אחד מהמשתנים binky,‏ module1 ו-module2 יש 'מרחב שמות' משלו, שהוא, כפי שאפשר לנחש, קשר בין שם משתנה לאובייקט.

לדוגמה, יש לנו את מודול sys הסטנדרטי שמכיל כמה מתקני מערכת סטנדרטיים, כמו רשימת argv והפונקציה exit(). אחרי שמצהירים על import sys, אפשר לגשת להגדרות במודול sys ולהשתמש בהן באמצעות השם המלא שלהן, למשל sys.exit(). (כן, גם ל-sys יש מרחב שמות!)

  import sys

  # Now can refer to sys.xxx facilities
  sys.exit(0)

יש טופס ייבוא נוסף שנראה כך: "from sys import argv, exit". כך אפשר להשתמש ב-argv וב-exit() לפי השמות הקצרים שלהם. עם זאת, מומלץ להשתמש בטופס המקורי עם השמות המלאים, כי כך קל יותר לקבוע מאיפה הגיעה פונקציה או תכונה.

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

  • ‫sys – גישה אל exit(),‏ argv,‏ stdin,‏ stdout וכו'
  • ‫re — regular expressions
  • ‫os – ממשק מערכת הפעלה, מערכת קבצים

אפשר למצוא את התיעוד של כל המודולים והחבילות של הספרייה הרגילה בכתובת http://docs.python.org/library.

עזרה אונליין, help() ו-dir()

יש מגוון דרכים לקבל עזרה בנושא Python.

  • מבצעים חיפוש ב-Google, מתחילים במילה python, כמו python list או python string lowercase. התשובה מופיעה בדרך כלל בהתאמה הראשונה. משום מה, נראה שהטכניקה הזו עובדת טוב יותר ב-Python מאשר בשפות אחרות.
  • באתר הרשמי של מסמכי Python –‏ docs.python.org – יש מסמכים באיכות גבוהה. למרות זאת, לעיתים קרובות אני מוצא שחיפוש ב-Google של כמה מילים הוא מהיר יותר.
  • יש גם רשימת תפוצה רשמית של Tutor שמיועדת במיוחד למתחילים ב-Python או בתכנות.
  • באתרים StackOverflow ו-Quora אפשר למצוא הרבה שאלות (ותשובות).
  • משתמשים בפונקציות help()‎ ו-dir()‎ (ראו בהמשך).

בתוך מתורגמן Python, הפונקציה help() מאחזרת מחרוזות תיעוד למודולים, לפונקציות ולשיטות שונות. מחרוזות התיעוד האלה דומות ל-javadoc של Java. הפונקציה dir()‎ מאפשרת לכם לדעת מהם המאפיינים של אובייקט. בהמשך מפורטות כמה דרכים להפעיל את הפונקציות help()‎ ו-dir()‎ מהמפרש:

  • help(len) – מחרוזת העזרה של הפונקציה המובנית len(). שימו לב: המחרוזת היא len ולא len(), שזו קריאה לפונקציה, וזה לא מה שאנחנו רוצים.
  • help(sys) — מחרוזת עזרה למודול sys (צריך לבצע קודם import sys)
  • dir(sys)dir() דומה ל-help() אבל הוא רק נותן רשימה מהירה של הסמלים המוגדרים שלו, או 'מאפיינים'
  • help(sys.exit) – מחרוזת עזרה לפונקציה exit() במודול sys
  • help('xyz'.split) – מחרוזת עזרה לשיטה split() לאובייקטים מסוג מחרוזת. אפשר להתקשר אל help() עם האובייקט עצמו או עם דוגמה לאובייקט הזה, בתוספת המאפיין שלו. לדוגמה, להתקשר אל help('xyz'.split) זהה להתקשרות אל help(str.split).
  • help(list) — מחרוזת עזרה לאובייקטים של list
  • dir(list) – הצגת מאפייני האובייקט list, כולל השיטות שלו
  • help(list.append) — מחרוזת עזרה לשיטה append() לאובייקטים מסוג list