Professional Documents
Culture Documents
Python Hebrew Booklet 2.5
Python Hebrew Booklet 2.5
Python Hebrew Booklet 2.5
5
תוכן עניינים
פרק _:1מבוא 4 ..........................................................................................................
מהי 4.................................................................................................................................... ?Python
Pythonלעומת 4...............................................................................................................................C
הפעלת 4................................................................................................................................ Python
עמוד 2
Python 2.5
עמוד 3
Python 2.5
פרק _:1מבוא
מהי ?Python
Pythonהיא שפת Scriptingנוחה לשימוש שהפכה למאוד פופולרית בשנים האחרונות Python .נוצרה בשנות ה90-
המוקדמות ע"י גידו ואן רוסם ) (Guido van Rossumב ,Stichting Mathematisch Centrum-כשפת המשך לשפה שנקראה
.ABCבשנת 1995גידו המשיך את פיתוח בחברה ששמה ,(CNRI) Corporation for National Research Initiativesואף
הוציא מספר גירסאות של השפה .במאי 2000גידו וצוות הפיתוח של Pythonעברו ל BeOpen.com-כדי לייסד את
" ."BeOpen Python Labs teamבאוקטובר 2000בצוות עבר ל ,Digital Creations-וב 2001-נוסד Python Software
,(PSF) Foundationשהוא ארגון ללא מטרות רווח Digital Creations .היא אחת המממנות של .PSFכל מה שקשור ל-
Pythonהוא ,Open Sourceכלומר הקוד פתוח ונגיש לכולם ,ולכל אחד מותר לבצע בו שינויים ולהפיצו מחדש .אתר
הבית של PSFהוא .http://www.python.org/psf/
כאשר אומרים "שפת "Scriptingמתכוונים שאין צורך בהידור ) (Compilationוקישור ) (Linkageשל תוכניות .בPython-
פשוט כותבים ומריצים .כל סביבת עבודה של Pythonמגיעה עם Interpreterשמאפשר כתיבה ישירה של פקודות אליו,
אפילו בלי צורך לכתוב תוכנית או קובץ .בצורה כזו מאוד נוח להתנסות בשפה ,לבצע חישובים מהירים )עוד תכונה חזקה
של (Pythonולבדוק דברים קטנים במהירות ,בלי הצורך לכתוב תוכנית שלמה.
ל Pythonיתרונות רבים על פני שפות Scriptingאחרות .ראשית Python ,פשוטה בהרבה ללימוד ,לקריאה ולכתיבה
משפות אחרות )שחלקן מתעללות במתכנת ובמי שצריך לקרוא קוד כתוב( Python .גורמת לקוד שכתוב בה להיראות
מסודר ,ברור ומובנה )נראה כיצד מאוחר יותר( .מודל המימוש של השפה ברור בהרבה ממימוש שפות אחרות ,ובכך
תורם להבנת השפה ולשליטה בה.
שנית Python ,מכיל בתוכו אוסף מכובד מאד של ספריות סטנדרטיות ,כלומר ספריות שניתן להסתמך על קיומן בכל
מימוש של .Pythonבין הכלים השימושיים ניתן למצוא יכולות התמודדות עם קבצים דחוסים ) ,(zipתכנות לתקשורת
,TCP/IPכלים מרובים לתכנות בסביבת האינטרנט ,ממשק משתמש גרפי ) ,(GUIספריות לטיפול בטקסט ומחרוזות )כמו
כן עיבוד טקסטואלי למיניו( ,עבודה עם פונקציות מתקדמות של מערכת ההפעלה ,תמיכה בתכנות מבוזר ) Distributed
,(Developmentיכולת טיפול אינטנסיבית בקבצי XMLונגזרותיהם ,ועוד הרבה הרבה הרבה דברים! כמו כן Python
מטפלת במידע בינארי )מידע שאינו טקסטואלי( בצורה הרבה יותר טובה משפות מתחרות.
בנוסף ,כמו שניתן לכתוב תוכניות קטנות ב ,Python-קיימים בה כלים לכתיבת תוכניות גדולות ,ואף מודולים שלמים.
Pythonתומכת בתכנות מכוון עצמים ) ,(OOPחריגות ) ,(Exceptionsמבני נתונים ) (Data Structuresוקישוריות גם ל ,C-או
כל שפה שניתן לקרוא לה מ.C-
דבר נוסף שכדאי לדעת על Pythonהוא שיש לה מספר גרסאות .אמנם יש תאימות לאחור ,אבל בגרסאות החדשות
) 2.0ומעלה( יש הרבה תכונות חדשות ונוחות לשימוש .בחוברת זו נלמד את גרסה .2.5
Pythonלעומת C
אוקיי ,אז Pythonהיא שפה מגניבה שמאפשרת לעשות הרבה דברים בקלות ובמהירות ,לא צריך להסתבך עם
קומפיילרים ולינקרים ואפילו אפשר לא לכתוב את התוכניות בקבצים אם רוצים .אבל ,זה לא אומר שמפסיקים
להשתמש ב:C-
בראש ובראשונה Python ,היא שפת – Scriptingמה שאומר ש Python-רצה לאט יותר מ .C-כאשר נעדיף ביצועים על
מהירות כתיבת הקוד )או נוחות הכתיבה( ,נשתמש ב C-או כל שפה מקומפלת אחרת .בנוסף ,ניתן לכתוב מודולי הרחבה
ל Python-באמצעות Cו ,C++-ולכן שפות אלה הן בעלות שימוש רב גם בעת העבודה עם .Python
הפעלת Python
דבר אחרון שנעשה במבוא המצומצם שלנו הוא להפעיל את .Python
כיום Python ,מופצת יחד עם כל ה-Distribution-ים הפופולריים של ,RedHat, Slackware, Corel, Debian) Linuxועוד,(...
וניתן להוריד סביבות פיתוח של Pythonל) Windows-למשל ActivePythonשל ,ActiveStateגירסה 2.5ומעלה(.
כמו כן ,ניתן לבקר באתר www.python.orgולמצוא שם גרסאות מוכנות ל – Windowsכולל התקנה נוחה ופשוטה.
שימו לב :הנכם מקבלים ,בנוסף לחוברת זו ,גם חוברת ודיסק של לינוקס .ניתן להפעיל את pythonמהלינוקס
שקיבלתם.
כדי להפעיל את Pythonבלינוקס ,פשוט מקלידים את הפקודה pythonב console-ומקבלים את ה prompt-של :Python
$ python
)Python 2.5.2 (r252:60911, Apr 21 2008 11:12:42
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>>
כדי להפעיל את Pythonמחלונות ,צריך כמובן להתקין את אחת מסביבות העבודה שקיימות כיום לחלונות .אם התקנת
את הסביבה המצויינת למעלה ,ניתן להפעיל את הסביבה מStart Programs ActiveState ActivePython Python -
.Interpreter Shell
עמוד 4
Python 2.5
במהלך כל החוברת ,הדוגמאות שיירשמו יילקחו מסביבת העבודה בלינוקס ,אבל אין שום הבדל בין שתי סביבות
העבודה )בהנחה שעובדים עם גירסה עדכנית – 2.2ומעלה(.
חשוב מאוד! חוברת זו מלמדת את גירסה 2.5של ,Pythonולכן כאשר אתם מורידים גירסה של ,Pythonיש לבדוק שזו
גירסה 2.5ומעלה.
עמוד 5
Python 2.5
>>>
ב prompt-הזה ניתן למעשה לכתוב כל פקודת ,Pythonוהיא תורץ כל עוד היא חוקית ותקינה.
אבל הדבר הראשון שננסה הוא לא פקודה ,אלא ביטוי .נרשום את הביטוי :1+1
>>> 1+1
2
הביטוי נכון ,אבל זה לא ממש מרשים ...אמרנו של Python-יש יכולות חישוב מהירות .בואו ננסה משהו קצת יותר מסובך:
>>> 2**2**2**2**2
את התוצאה כבר לא נרשום כאן ...הסבר מלא על השורה )ולמה רשמנו 2Lבמקום 2רגיל( יופיע בפרק על טיפוסי
נתונים .אם הרצת את השורה האחרונה על המחשב שלך ,לקח לו כמה שניות להגיע לתוצאה ,אבל הוא כתב על המסך
ממש הרבה מספרים .מה שעשינו בשורה האחרונה היה:
2
22
2
2
וכמו שבוודאי הבנת ** ,זה חזקה.
Pythonיודע גם לטפל במחרוזות :מחרוזות נכתבות כרצף של תווים בין שני תווים של גרש בודד ) ' ( ,או בין שני תווים
של גרשיים )"( .כמובן ,אי-אפשר לשלב )מחרוזת שנפתחת בגרש אחד ונסגרת בגרשיים(.
>>> 1+1
2
>>> _+2
4
>>> _
4
>>> _**2
16
>>> x=5
)>>> type(x
>’<type ‘int
בדוגמה הזאת נוצר משתנה בשם ,xוערכו ההתחלתי הוא .5כיוון ש 5-הוא מספר שלם )יש לשים לב להבדל בין 5ל-
,5.0כמו ב (C-סוג המשתנה הוא .int
את סוג המשתנה מקבלים באמצעות הפונקצייה המובנית ,typeהמקבלת בסוגריים את המשתנה ומחזירה את הסוג
שלו.
כאשר מציבים למשתנה ערך חדש ,גם סוג המשתנה מתעדכן בהתאם .אם xהיה intעד עכשיו ,ונציב לתוכו ערך חדש,
סוג המשתנה ישתנה בהתאם לערך החדש:
עמוד 6
Python 2.5
>>> x=5.0
)>>> type(x
>’<type ‘float
>>> x=5
>>> y=x
>>> y
5
בנוסף ,כדי להדפיס ערך של משתנה ,ניתן להשתמש בפקודה ) printזה כבר שימושי בתוך תוכניות ,לא ב:(Interpreter-
הפקודה ,printבנוסף לפקודות אחרות )יש להבדיל בין פקודות לבין פונקציות( מופיעה ללא סוגריים .זאת משום שזו
פקודה ולא פונקציה .ההבדל בין פונקציה לפקודה הוא שפקודה היא כל מילת מפתח של Pythonשאיתה כותבים את
בקרת הזרימה של התוכנית )הכוונה לפקודות ,print ,while ,ifוכו'( .פונקציה היא אוסף של פקודות ,פונקציות אחרות
ופונקציות מובנות שאותן אספנו ביחד כדי ליצור פעולה בסיסית.
נקודה נוספת שיש לציין היא ש Python-היא ,Case-Sensitiveכלומר יש הבדל בין משתנה בשם aלבין משתנה בשם – A
אלו הם שני משתנים שונים ,משום שהאות Aאיננה האות .aחוקים אלה הם כמו החוקים של .C
בנוסף לטיפוסים המוכרים ב ,C-ב Python-יש מספר טיפוסים מובנים ) (Built Inנוספים )את כולם נסקור בפרק הבא(.
אחד מהם הוא רשימה .יצירה רשימה נעשית באופן הבא:
כעת יש רשימה בשם ) lהאות Lקטנה( שמכילה את המספרים .3 ,2 ,1האינדקס של האיבר הראשון הוא אפס ,וניתן
להתייחס לרשימה כולה או לאיבר בודד בה:
>>> l
][1, 2, 3
]>>> l[0
1
עמוד 7
Python 2.5
>>> y
][1, 6, 7
כפי שניתן לראות ,יצרנו רשימה במשתנה ,xוהשמנו אותה למשתנה .yכתוצאה מכך ,לא נוצרה רשימה חדשה ,אלא
הצבעה לרשימה הקיימת .כאשר שינינו את הרשימה הקיימת ,כל מי שהצביע אליה הושפע מכך.
עמוד 8
Python 2.5
אז xהוא משתנה מסוג .intאם ננסה להכריז על מספר שלם שחורג מהתחום ,הוא יומר אוטומטית ל:long-
>>> 4389678923476892347
4389678923476892347L
כמו כן ,אם כבר קיים משתנה שלם ומנסים לחשב באמצעותו ערך שחורג מהתחום של משתנה שלם ,הוא יומר
אוטומטית למשתנה ארוך:
>>> 2**30
1073741824
>>> _*2
2147483648L
כלומר ,מספרים שלמים מתנהגים כמו ב C-מבחינת גבולות וייצוג ,אבל אין באמת מגבלה על גודל מספר בפיתון ,מכיוון
שבמידת הצורך מספר מומר ל long-שיכול לתמוך במספר בכל גודל שהוא.
הכוונה ב"כל גודל שהוא" היא ש Python-דואגת לשמור משתנה בגודל הדרוש כך שיחזיק את כל המספר ,ודואגת
לשנות את גודל המשתנה בהתאם עם כל שינוי של ערך המספר.
למרות שאין בכ צורך אמיתי ,אפשר ליצור משתנה ארוך כמו שיוצרים משתנה שלם רגיל .ההבדל היחיד הוא הוספת
האות Lאחרי המספר ) Lעבור .(Longאין משמעות להוספת Lקטנה או גדולה ,אבל מומלץ בתור הרגל לכתוב תמיד L
גדולה – הרבה יותר ברור שהכוונה ליצירת מספר ארוך ,ולא הספרה אחת ).(1
>>> 2L
2L
)_(>>> type
>'<type 'long
סוג המשתנה הבא והאחרון לסעיף זה הוא המספר העשרוני .ייצוג המספר העשרוני שונה ממספר שלם ,ולכן חלות עליו
מגבלות מסוימות .גם למספר עשרוני יש גבולות )הגבולות תלויים במימוש של המספר העשרוני – האם זה מספר
עשרוני של 32-bitאו של ,(64-bitאבל אין לו טיפוס מקביל לשם הארוך.
יצירת משתנה עשרוני היא כמו ב ,C-ע"י כתיבת מספר עם נקודה עשרונית ושבר )אפשר שבר ,0רק כדי להצהיר שזה
מספר עשרוני(.
>>> 21.0
21.0
כמו כן ,כאשר משלבים בחישוב מסוים מספר עשרוני ,התוצאה הופכת אוטומטית להיות עשרונית ,אלא אם כן
משתמשים באחת מפונקציות ה) casting-אותן נראה בהמשך(.
>>> 1.0/2
0.5
כאשר משלבים בחישוב מספר ארוך ומספר עשרוני יכול להיווצר מקרה בו התוצאה גדולה מדי כדי להיות מיוצגת בצורה
עשרונית .אם ניתן לחשב את התוצאה ולאחסן אותה ,לא תהיה שום בעיה:
>>> 21L/7.0
3.0
עמוד 9
Python 2.5
אבל אם לוקחים מספר ממש ממש ארוך ומבצעים חישוב עם מספר עשרוני ,תיווצר שגיאה:
>>> 0xBEE
3054
Pythonכמובן יציג את המספר בייצוג עשרוני ,וזוהי דרך מצויינת להמיר מבסיס לבסיס ממש מהר .אפשר לעשות גם את
הפעולה ההפוכה ,בעזרת הפונקצייה המובנית )( hexשמקבלת מספר ומחזירה מחרוזת המכילה את המספר בייצוג
הקסהדצימלי:
>>> 0xBEE
3054
)_(>>> hex
’‘0xbee
ייצוג מספרים בבסיס (Octal) 8נעשה ע"י הוספת אפס לפני המספר אותו רוצים לייצג.
>>> 045
37
כמו הפונקצייה )( ,hexקיימת פונקצייה מובנית בשם )( octשממירה מספר דצימלי למחרוזת בייצוג אוקטלי:
)>>> oct(45
’‘055
אופרטורים :בתת-סעיף זה נראה את כל האופרטורים שיכולים לפעול על מספרים .יש לשים לב כי יש אופרטורים
שפועלים בין מספרים לבין טיפוסים אחרים ,אבל כאן נציג רק את האופרטורים הקשורים למספרים.
חיבור ) ,(+חיסור ) (-וכפל )*( פועלים כמו בשפת .Cכלומר ,כאשר יש פעולה בין 2אופרנדים זהים ,התוצאה היא
מהסוג של האופרנדים .כאשר האופרנדים לא זהים ,התוצאה תהיה מהטיפוס ה"יותר מתקדם" :בכל פעולה בה מעורב
מספר עשרוני התוצאה תהיה עשרונית .אם בפעולה לא מעורב משתנה עשרוני ,אבל מעורב משתנה ארוך ,התוצאה
תהיה ארוכה.
חילוק ) (/גם פועל כמו ב ,C-וכאשר ישנם טיפוסי נתונים שונים שמעורבים בחישוב ,הכלל לגבי חיבור חיסור וכפל קובע
גם כאן .כאשר יש חילוק בין שלמים ,התוצאה תמיד שלמה .לעומת זאת ,אם רוצים תוצאה עשרונית ,חייב להיות מעורב
מספר עשרוני.
אם רוצים לאלץ את Pythonלייצר תוצאה עשרונית ,אבל משתמשים רק במשתנים שלמים ,ניתן להשתמש בפונקצייה
)( floatכדי להמיר את אחד המשתנים השלמים למספר עשרוני ,ועל-ידי כך לגרום לתוצאה להיות עשרונית:
>>> x = 8
>>> y = 3
>>> x/y
2
>>> float(x) / y
2.6666666666666665
כמובן ,כדי לקבל תוצאה עשרונית נכונה בדוגמה האחרונה ,לא ניתן לעשות את הדבר הבא:
)>>> float(x/y
2.0
מודולו ) (%לא מתנהג כמו בשפת ,Cהוא קצת יותר משוכלל .אופרטור המודולו יכול לקבל מספרים שלמים רגילים,
ארוכים וגם משתנים עשרוניים .התוצאה במקרים בהם המספרים שלמים וארוכים ברורה ,היא מחזירה את השארית של
החלוקה בין שני המספרים ,והטיפוס )כמו בכל האופרטורים האחרים( יהיה לפי הכלל של חיבור וחיסור.
במקרה בו אחד האופרטורים הוא משתנה עשרוני ,התוצאה מחושבת כמספר שלם )כלומר ,מחושב המודולו של שני
הפרמטרים ,והתוצאה הנה בצורת מספר עשרוני(.
דוגמה להתנהגות זו:
בדוגמה הראשונה חושב המודולו של המספרים 3ו ,6-כי הם בסך-הכל שלמים שמיוצגים בצורה עשרונית .בדוגמה
השניה חושב המודולו ההפוך )והתוצאה בהתאם(.
בדוגמה השלישית לעומת זאת ,חושבה השארית של 2.5לחלק ל.3.5-
בולואנים
טיפוסים בוליאנים הם האחים החורגים של המספרים .טיפוס בוליאני יכול לקבל רק את הערכים Trueאו ,False
ובהרבה פונקציות של פיתון )וכמובן גם בפונקציות שניצור בעצמנו ,כמו שעוד נראה בהמשך( מקובל להחזיר ערכים
אלה כאשר הערך יכול להיות "כן" או "לא".
לדוגמה:
>>> 5 == 6
False
ניסינו לבדוק האם 5שווה ל) 6-האופרטור == הוא בכל אותה משמעות כמו ב ,(C-וקיבלנו את התשובה "לא".
הסיבה שבוליאנים דומים למספרים היא שפיתון יודעת לתרגם ,במידת הצורך ,בין בוליאנים למספרים שלמים רגילים.
Trueמתורגם ל False ,1-מתורגם ל ,0-והתרגום מתבצע רק כאשר מנסים לבצע פעולות חשבוניות על בוליאנים ,למשל:
מחרוזות
מחרוזות ב Python-מיוצגות בצורה הפוכה מ :C-ב ,C-מחרוזת היא אוסף של תווים .ב ,Python-מחרוזת היא טיפוס נתונים
מובנה ,ותו הוא מחרוזת באורך .1ייצוג זה מקל מאוד על העבודה עם מחרוזות – מה שמצריך ב C-שימוש בפונקציות של
הספריה > <string.hמצריך כאן שורת קוד אחת קצרצרה.
כמו שניתן לראות ,מחרוזות ניתן ליצור בשתי צורות :המחרוזת נמצאת בין 2תווים של גרשיים או בין 2תווים של גרש
בודד .כמובן ,אם מחרוזת נמצאת בין 2תווים של גרשיים ,ניתן לכלול בה גרש בודד )בלי ’\‘( ,ולהפך .כמובן ,אפשר
להשתמש בתו ה ‘\’-כדי לכלול את התווים האלה במחרוזות.
כמו כן ,כל הצירופים של ’\‘ ותו אחריהם מ C-תקפים ב ‘\n’ :Python-לשורה חדשה ‘\r’ ,לתחילת השורה‘\b’ ,
לחזור תו אחד אחורה ‘\t’ ,עבור טאב ,וכו'...
כמו כן ,ב Python-יש מנגנון מובנה לפירמוט ) (Formattingשל מחרוזות )מה שעושה )( .(printfזה נעשה ע"י כתיבת כל
ה %-המוכרים כבר מ C-לתוך המחרוזת וכתיבת כל הפרמטרים אחרי המחרוזת:
בפועל ,אחרי המחרוזת יש את התו ’ ,‘%ואחריו אנחנו מעבירים ) Tupleהטיפוס יוצג בהמשך פרק זה( של כל הפרמטרים,
לפי הסדר .אם מעבירים מספר לא מדויק של פרמטרים )למשל ,רשמנו רק %dבמחרוזת ,והעברנו שני פרמטרים( ,יש
שגיאה .כמו כן ,אם מעבירים פרמטר שהוא לא מהסוג שהעברנו ב) %-מחרוזת עבור ,%dוכו'( ,גם יש שגיאה.
פירמוט המחרוזות של Pythonהרבה יותר מוגן ונוח מזה של ,Cהוא לא מצריך קריאה לפונקציה ,וניתן להכניס אותו בכל
מקום ,אפילו בתוך קריאה לפונקציה )בפרמטר ,במקום להכניס סתם מחרוזת רגילה ,מכניסים מחרוזת מפורמטת(:
עמוד 11
Python 2.5
כעת נסקור כמה פונקציות שימושיות מאוד .חלק מפונקציות אלה מובנות ב Python-עצמה ,וחלקן של טיפוס המחרוזת.
)( chrמקבלת כקלט מספר המייצג את ערך ה ASCII-של תו מסוים ,ומחזירה מחרוזת ובה תו אחד שערך ה ASCII-שלו
הוא הערך שקיבלה הפונקציה כפרמטר:
)>>> chr(97
’‘a
)>>> chr(65
’‘A
)( strלעומתה מקבלת משתנה ומנסה להמיר אותו למחרוזת .הפונקציה מסוגלת לקבל יותר מטיפוסים בסיסיים ,אפילו
רשימות ומילונים )נראה בהמשך(:
)>>> str(1234
’‘1234
)]>>> str([1,2,3
’]‘[1, 2, 3
הפונקציה )( splitעושה דבר דומה ל split-ב ,Perl-כאשר היא מקבלת מחרוזת ותו ,ומפצלת את המחרוזת לרשימה ,כאשר
התו שהיא קיבלה מפריד בין האיברים ברשימת הפלט .לדוגמה:
הפונקציה )( ,joinלעומתה ,עושה בדיוק ההפך – היא מקבלת רשימה ,ומופעלת על מחרוזת .הפלט הוא מחרוזת
המורכבת מכל איברי הרשימה ,והמחרוזת הנתונה ביניהם:
דבר שלא ניתן לעשות עם מחרוזות הוא לשנות את ערכו של תו בודד .הסיבה לכך נעוצה במימוש של ,Pythonשכמו
שכבר ציינו בנוי ממצביעים .מחרוזת היא מצביע לעצם שמכיל את המחרוזת .הביטוי הבא:
’>>> s = ‘ugifletzet
]>>> s[1
’‘g
מחזיר תת-מחרוזת שמכילה רק תו אחד במחרוזת ,ולא מאפשר השמה לערך זה .אם נרצה לשנות את המחרוזת ,ניאלץ
להזין את המשתנה בערך חדש לגמרי.
בסעיף על רשימות נראה שבאמצעות Slicingניתן לקבל תת-מחרוזת )ולא רק תו אחד(.
בנוסף ,קיימות במחרוזות פונקציות לשינוי ה Capitalization-של מחרוזות ,כלומר כל נושא האותיות הקטנות והגדולות
באנגלית .הפונקציה )( capitalizeמקבלת מחרוזת ומחזירה את אותה המחרוזת ,כאשר האות הראשונה במילה הראשונה
מתחילה באות גדולהף והשאר קטנות.
הפונקציה )( upperמחזירה מחרוזת בה כל האותיות גדולות .הפונקציה )( lowerעושה אותו הדבר ,רק מחזירה מחרוזת
בה כל האותיות קטנות.
stripמקבלת מחרוזת ומסירה ממנה את תווי ה whitespace-שנמצאים בקצוות המחרוזת .למשל ,הנה המחרוזת הבאה
אחרי :strip
הפונקציה indexמקבלת מחרוזת ותת-מחרוזת ,ומחזירה את המקום הראשון בו תת-המחרוזת מופיעה בתוך המחרוזת.
אם לא נמצא מקום כזה ,הפונקציה זורקת שגיאה )יילמד בהמשך בפרק על .(Exceptionsהפונקציה findעושה בדיוק
אותו הדבר ,אבל אם היא לא מוצאת את תת-המחרוזת היא מחזירה .-1
עמוד 12
Python 2.5
מחרוזות Unicode
דבר נוסף שנתמך ע"י Pythonהוא מחרוזות – Unicodeמחרוזות אלו הן מחרוזות בהן כל תו מיוצג ע"י שני בתים ,וכך ניתן
לייצג יותר תווים בטיפוס אחד .התמיכה ב Unicode-היא לא לשימוש שוטף ב ,Python-אלא יותר עבור תאימות עם
מודולים שיש להם ממשק הדורש .Unicode
מחרוזת Unicodeנוצרת בדיוק כמו מחרוזת רגילה ,רק שלפני המחרוזת יש את התו ’:‘u
רשימות
רשימה היא טיפוס שמחזיק טיפוסים אחרים ,שומר על הסדר שלהם ,מאפשר להוסיף ,להסיר ולקבל איברים מכל מקום
ברשימה ,וניתן לעבור על כל אחד מהאיברים שברשימה .ב ,Python-רשימה היא טיפוס מובנה ,ובנוסף ,רשימה אחת
יכולה להחזיק איזה טיפוס שנרצה ,ואפילו כמה סוגי טיפוסים בבת-אחת.
יצירת רשימה נעשית ע"י רשימה בה איבריה מופיעים בין סוגריים מרובעות:
]>>> x = x + [6
>>> x
][1, 2, 3, 4, 5, 6
)>>> x.append(6
>>> x
][1, 2, 3, 4, 5, 6
הדרך הראשונה יכולה לגרום להקצאה של רשימה חדשה – וזה בגלל שיכול להיות שנוצרת רשימה חדשה אליה
מוכנסים האיברים של שתי הרשימות המחוברות ,ובה משתמשים מאותו הרגע .הדרך השניה מבטיחה שלא תהיה יצירה
של רשימה חדשה ,אלא שימוש ברשימה הקיימת והוספת איבר אליה.
כמו כן ,ניתן "לשכפל" רשימה ,כלומר להעתיק את אותה הרשימה מספר פעמים:
>>> x * 3
][1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6
– Slicingאו בעברית "חיתוך" ,היא הפעולה בה אנחנו לוקחים רשימה קיימת ויוצרים ממנה רשימה חדשה ,ע"י חיתוך של
חלק מהרשימה המקורית .ה Slicing-נעשה ע"י ציון האינדקסים של תחילת וסיום החיתוך .לדוגמה ,הנה רשימה חדשה
שמורכבת מאינדקסים 1עד )לא כולל!( :3
]>>> x[1:3
][2, 3
המספר הראשון )לפני הנקודותיים( הוא האינדקס ממנו מתחיל החיתוך ,והמספר השני הוא האינדקס שבו יסתיים
החיתוך ,אבל הוא בעצמו לא ייכלל ברשימה הנוצרת.
מטעמי נוחות ,כאשר רוצים להעתיק קטע רשימה שמתחיל מיד בהתחלה שלה ,או מסתיים מיד בסופה ,אין צורך לציין
את האינדקס הראשון )תמיד (0או את האינדקס האחרון )תמיד אורך הרשימה( ,וניתן להשמיט אותו .לדוגמה ,הנה
הרשימה עד אינדקס :4
]>>> x[:4
][1, 2, 3, 4
]>>> x[4:
][5, 6
כמו כן ,כאשר משמיטים גם את התחלת הרשימה וגם את סופה ,מקבלים את כל הרשימה:
]>>> x[:
][1, 2, 3, 4, 5, 6
עמוד 13
Python 2.5
לכאורה זהו בזבוז מקום ,הרי ניתן לרשום סתם xולקבל את הרשימה כמו שהיא ,אבל מיד נראה למה זה טוב.
כמו שכבר ציינו מקודם ,ב Python-משתנים הם בסך-הכל מצביעים ,כלומר שם של משתנה הוא מצביע לטיפוס מסוים.
כאשר משתנה מצביע לטיפוס כלשהו – בין אם זה טיפוס מובנה או טיפוס שנוצר ע"י המשתמש )בהמשך החוברת נראה
כיצד עושים את זה( ,הוא מצביע לכתובת מסוימת בזיכרון .כאשר מבצעים השמה )עושים =( בין שני משתנים,
והמשתנים הם לא מטיפוס "פשוט" )לא מספר( ,ההשמה למעשה מעתיקה את המצביע ממשתנה אחד למשתנה
האחר.
בדוגמה הבאה ניצור רשימה בשם ,xנשים אותה במשתנה ,yנוסיף איבר לרשימה yונראה כיצד הרשימה xמשתנה גם
כן:
הדוגמה האחרונה מראה תכונה חזקה מאוד של – Pythonאין בה צורך במצביעים ,כיוון שאין מה משהו אחר ממצביעים.
ב Python-כל משתנה הוא מצביע ,והשמה היא העתקה של המצביע ,ולא התוכן )למעט מספרים ומחרוזות(.
כאשר מעבירים פרמטרים לפונקציה ,הדבר עובד באותה הצורה – שינוי של פרמטר שיועבר לפונקציה הוא שינוי
המשתנה המקורי שהועבר לפונקציה.
תכונה זו מאוד נוחה ,אבל לפעמים נרצה ליצור עותק של משתנה קיים – מה נעשה אז? השיטה הכי טובה היא
להשתמש ב Slicing-אותו למדנו מקודם ,ובו ראינו שכאשר עושים Slicingשכולל את כל האיברים )בלי ציון התחלה ובלי
ציון סוף( ,נוצר עותק חדש של העצם המקורי .כעת נראה איך תתבצע ההשמה עם :Slicing
יש לציין שה Slicing-יעבוד עבור מחרוזות ,רשימות-Tuple ,ים ,וכל טיפוס סדרתי.
Tuples
Tupleהוא טיפוס של רשימה קבועה .הטיפוס נועד לשימוש במצבים בהם אין אפשרות ליצור רשימה ,או כאשר יש צורך
ברשימה שאורכה לא משתנה .כמו כן ,לא ניתן לשנות אף אחד מהאיברים ב.Tuple-
יצירת Tupleנעשית בדיוק כמו יצירת רשימה ,רק שבמקום סוגריים מרובעות משתמשים בסוגריים עגולות:
קבלת איבר מה Tuple-היא כמו איבר מרשימה – ע"י סוגריים מרובעות:
]>>> t[0
1
אבל אם ננסה לשנות איבר ברשימה ,לא יהיה ניתן לעשות זאת:
עמוד 14
Python 2.5
צריך לדעת שלא ניתן לחבר Tupleלרשימה ,משום שהם אינם טיפוסים מאותו הסוג.
בנוסף לחיבור ,ניתן לעשות Slicingעל :Tuple
ובדומה לרשימה ,השמת Tupleלא תיצור עותק שלו אלא תעביר מצביע .ליצירת עותק יש להשתמש ב.Slicing-
– Tuple Assignmentsכאשר מבצעים השמה בין שני -Tupleים Python ,מבצעת השמה בין כל שני איברים מתאימים ב-
-Tupleים .לדוגמה:
בדוגמה האחרונה נוצרו שני -Tupleים – באחד שלושה משתנים ,ובשני שלושה ערכים Python .עשתה השמה בין aל,1-
בין bל 2-ובין cל .3-בצורה כזו ,נוצרו שלושה משתנים עם שלושה ערכים התחלתיים בשורה אחת .כמובן ,לא ניתן
לבצע השמה בין שני -Tupleים שמכילים "סתם" איברים שלא ניתן להשם ביניהם:
אחד הטריקים שניתן לבצע בעזרת Tuple Assignmentsהוא החלפת ערכי שני משתנים בשורה אחת:
>>> x, y = y, x
השורה האחרונה פשוט החליפה בין הערכים של xו ,y-ללא צורך במשתנה זמני.
השימוש העיקרי ב-Tuple-ים ב Python-הוא עבור אחסון מידע שאין צורך לשנותו )או שיש צורך לוודא שהוא לא ישתנה(.
כמו כן ,בהמשך החוברת נראה שפונקציה יכולה להחזיר רק איבר אחד .משום ש Tuple-הוא טיפוס ,ניתן להחזיר
מפונקציה Tupleהמכיל מספר איברים ,וע"י כך להחזיר יותר מאיבר אחד מפונקציה.
מילונים
מילון ) (Dictionaryב Python-הוא טיפוס שמטרתו היא המרה בין מפתחות לבין ערכים )ב C++-משתמשים ב map-לכך(.
מילון למעשה מחזיק ערכים ,מפתחות ואת המיפוי ביניהם .כאשר המשתמש פונה למילון ,הוא מציין מפתח ,ומקבל
עבורו ערך .היתרון בכך הוא שבמקום מספרי אינדקסים )שלא ממש אומרים משהו למישהו( ,ניתן להשתמש בכל ערך
שרק רוצים ,אפילו מחרוזות.
כעת המילון dיודע שה"ערך" של 1הוא " ."Sundayבקשת ערך עבור מפתח מסוים נעשית כמו ברשימה:
]>>> d[1
''Sunday
כמו כן ,ניתן ליצור מפתחות שאינם מספרים .למשל ,ניצור מילון שממיר בין מדינה לבירה שלה:
בעת פנייה למילון נוכל לציין מייד את שם המדינה ולקבל את הבירה שלה:
]'>>> cap['France
''Paris
]'>>> cap['Japan
Traceback (most recent call last):
>File "<stdin>", line 1, in <module
KeyError: Japan
בנוסף לפעולות ההמרה ,ניתן לבדוק גם האם מפתח מסוים קיים במילון ,בעזרת האופרטור :in
)(>>> cap.keys
]'['Israel', 'Italy', 'USA', 'England', 'Russia', 'France
)(>>> cap.values
]'['Jerusalem', 'Rome', 'Washington', 'London', 'Moscow', 'Paris
עמוד 16
Python 2.5
num = 5
if num > 3:
print num * 2
בשורה הראשונה יצרנו משתנה בשם numשערכו .5השורה השניה היא שורת ה if-עצמה ,ומבנה ההתניה דומה לזה
שב) C-מיד נראה מספר הבדלים( .בסוף השורה השניה יש נקודותים .תו ה ':'-ב Python-מורה על פתיחת בלוק חדש,
בדיוק כמו הסוגריים-המסולסלות ב.C-
ואיך Pythonיודעת מתי בלוק נגמר? את זה היא עושה באמצעות תו ה .TAB-בדוגמה למעלה ,השורה השלישית מוזחת
ימינה ב TAB-אחד ,ולא סתם בשביל אינדנטציה )כמו ב .(C-האינדנטציה ) ,Indentationהפעולה בה בלוק שתלוי בבלוק
קודם ייכתב "פנימה" יותר בקוד ,במקרה הזה ימינה ב TAB-אחד( ב Python-היא חלק מהמבנה שלה ,ולכן אי-אפשר
לכתוב בלי להוסיף TABלפני כל שורה בבלוק.
דבר נוסף ,חשוב לשים את תו ה ,(‘\t’) TAB-ולא שום אלתור אחר ,כמו 4או 5רווחים Python .מחפש את תו ה,TAB-
וצריך להיזהר מלשכוח לשים אותו – אפשר לשבת שעות מול שגיאה שנראית לכאורה בקוד ולגלות בסוף ששכחת
לשים .TABכמו כן ,ישנם עורכי טקסט שממירים TABלמספר רווחים ,או עושים דברים יותר משונים .לכן עדיף
להשתמש בעורכים שאפשר לסמוך עליהם שלא יתעללו בקוד )כמו .(vi ,EditPlus ,Notepadיש לציין שכותבי ה
Interpreterבנו אותו בצורה יחסית אמינה ,שמסוגלת להתמודד גם עם רווחים בחלק מהמקרים .בכל אופן -כדאי לא
לבדוק את היכולת הזו וללכת על בטוח.
בנוסף לתנאים רגילים ,אפשר כמובן ליצור תנאים לוגיים .ב Python-קיימות מילות המפתח or ,andו ,not-בנוסף
לאופרטורים הלוגיים שלקוחים מ .C-השימוש הוא די פשוט:
num = 17
if (num > 5) or (num == 15):
”print “something
השימוש הוא זהה עבור not .andיכול לבוא לפני תנאי כדי להפוך אותו:
while
פקודת הלולאה whileמאפשרת חזרה על פקודות על עוד תנאי מסוים מתקיים .התנאי יכול להיכתב כמו תנאי ב.if-
דוגמה:
i = 0
while i < 10:
print i
i += 1
בפקודת whileב Python-קיים גם משפט .elseמשפט זה מתבצע במקרה ובו התנאי שניתן לביצוע הלולאה לא התקיים
אפילו פעם אחת ,ולא הייתה שום כניסה לגוף הלולאה:
>>> i = 40
>>> while i > 50:
... i -= 1
... print i
... else:
... '!print 'i is smaller than 50
...
!i is smaller than 50
for
לפני שנסקור את פקודת ,forנגדיר מהו רצף – רצף הוא עצם שמכיל בתוכו איברים ,בסדר מסוים ,כאשר העצם מכיל
פונקציות לקבלת כל-אחד מהאיברים )או פונקציות לקבלת איבר ראשון ואיבר עוקב לאיבר קיים(.
דוגמאות לרצפים שאנחנו כבר מכירים ב Python-הן רשימותTuple ,ים ,מחרוזות ומילונים.
פקודת הלולאה forשונה מזו שב for .C-ב Python-לא מבצעת לולאה על תחום ערכים )כמו ב (Pascal-או מאחדת בתוכה
משפט-אתחול ,תנאי ופקודה לביצוע בכל איטרציה )כמו ב.(C-
עמוד 17
Python 2.5
forב Python-מקבלת רצף )כלומר אוסף של איברים( ,ובכל איטרציה מתייחסת רק לאיבר אחד ברצף .ההתייחסות
לאיברים נעשית לפי הסדר בו הם מאוחסנים ברצף ,כאשר הסדר נקבע לפי טיפוס הנתונים הנתון )אף אחד לא מבטיח
סדר מסוים ,אלא אם כן זוהי אחת מהגדרותיו של טיפוס הנתונים(.
בדוגמה ,הלולאה יצרה משתנה בשם .dayבכל איטרציה day ,מקבל את האיבר הבא של .daysהפלט של הריצה יהיה:
Sun
Mon
Tue
Wed
Thu
Fri
Sat
חשוב לציין שבסיום ריצת הלולאה ,המשתנה dayימשיך להתקיים ,וערכו יהיה הערך האחרון שקיבל במהלך ריצת
הלולאה.
אם מריצים לולאת forעם רשימה ריקה ,כלום לא יקרה והמשתנה ) dayאו כל משתנה אחר שייכתב שם( לא ייווצר .יש
לשים לב שאם מריצים כמה לולאות ,אחת אחרי השניה ,עם אותו משתנה )נגיד ,(iובאחת הלולאות יש רשימה ריקה,
אין הדבר אומר שהמשתנה iיושמד .זה רק אומר שהוא לא ישתנה עקב הלולאה הזו .אם הלולאה הזו היא הלולאה
הראשונה i ,באמת לא ייווצר:
כאמור ,ניתן לעבור לא רק על רשימות ,אלא גם על מילונים ומחרוזות .להלן שתי דוגמאות להמחשת העבודה עם
מחרוזות ומילונים:
a
n
עמוד 18
Python 2.5
o
t
h
e
r
s
t
r
i
n
g
הערה לגבי מילונים – בהגדרת המילון לא נאמר שהסדר בו האיברים מופיעים בו הוא הסדר בו הם יאוחסנו או הסדר בו
הם יוחזרו בלולאת .forלכן ,אין להניח הנחות על סדר האיברים בטיפוס מסוים ,אלא אם נאמר אחרת בהדגרת הטיפוס.
הנחה שכן ניתן להניח )וגם זאת בזהירות רבה מאוד( היא שאם הרצנו לולאת forעל רשימה מסוימת ,והרשימה לא
השתנתה ,ריצת forנוספת על אותה הרשימה תתבצע באותו הסדר.
range, xrange
טוב ,אז יש לולאת forשיודעת לעבור על כל האיברים ברשימה .אבל מה אם כן רוצים לעשות לולאת forכמו ב ,C-אבל
לא רוצים להשתמש ב.while-
פתרון אחד )לא מומלץ( הוא ליצור רשימה שמכילה את כל המספרים שנרצה לעבור עליהם ,מ 0-עד .Xברור שפתרון
זה לא טוב ,אם Xמשתנה מריצה לריצה ,או אם הוא מאוד גדול.
Pythonפותרת עבורנו את הבעיה באמצעות הפונקציות המובנות rangeו .xrange-שתי הפונקציות עושות את אותו
הדבר ,אבל בצורה שונה range .יוצרת רשימה חדשה אם ערך התחלתי ,ערך סופי וקפיצה ) ,Stepההפרש בין כ"א
מהאיברים(.
מבנה הפונקציה :range
כלומר range ,יוצרת רשימה חדשה עבורנו .הרשימה יכולה למעשה להכיל כל סדרה חשבונית שהיא.
כמו כן ,ניתן לקחת את הרשימה שנוצרה ולהצביע אליה עם משתנה ,ואז נקבל רשימה חדשה בממש מעט קוד:
)k = range(1024
עמוד 19
Python 2.5
לעומת זאת ,מבחינת ביצועים range ,יותר יעילה מ ,xrange-משום שבעת קבלת האיברים מהרשימה xrange ,צריכה
לחשב את האיבר הבא בכל פעם ,בעוד rangeמחזירה רשימה מוכנה לשימוש ,וצריך רק להחזיר ממנה את האיבר הבא
)ואין צורך לחשב אותו( .השימוש בכל-אחת מהפונקציות תלוי במקרה בו משתמשים בהן ,והאם יש עדיפות לחיסכון
בזיכרון או לשיפור בביצועים.
המבנה של xrangeזהה לזה של .range
break, continue
משפט breakנועד ליציאה מלולאות forאו break .whileיוצא רק מבלוק ה for-או בלוק ה while-בו הוא נמצא ,ולא מסוגל
לצאת החוצה יותר מבלוק אחד.
משפט continueמורה להפסיק מיד את האיטרציה הנוכחית של הלולאה ולהתחיל את האיטרציה הבאה .כמו ,breakגם
continueיכול לצאת רק מבלוק הלולאה הקרוב אליו.
דוגמאות:
עמוד 20
Python 2.5
הפקודה יכולה לקבל פרמטר אחד )כמו בדוגמה האחרונה( או מספר פרמטרים מופרדים בפסיקים:
כפי שניתן לראות print ,מוסיפה אוטומטית רווחים בין הפרמטרים שנשלחים להדפסה .כמו כן ,ניתן לתת כפרמטר כל
סוג משתנה ,כל עוד ניתן להמיר אותו למחרוזת כדי להדפיס אותו.
בנוסף ,הפקודה תמיד מדפיסה תו סיום שורה בסופה ,אלא אם כן שמים פסיק בסוף השורה:
השימוש בלולאת forבדוגמה האחרונה היה כדי ששתי הפקודות יבוצעו אחת אחרי השניה ,כי אם נכתוב את הפקודות
אחרת אחרי השניה ב ,Interpreter-ה Interpreter-יירד שורה באופן אוטומאטי לאחר כל ביצוע פקודה ,כדי לחזור לסמן
של הכנסת הפקודה הבאה )>>>(.
קלט מהמקלדת
כאשר תוכנית מעוניינת לקבל קלט מהמשתמש )דרך המקלדת בד"כ( ,ניתן להשתמש בפונקציה המובנית .raw_input
הפונקציה מקבלת כפרמטר מחרוזת אותה היא מדפיסה למסך ,ואז היא ממתינה לשורת קלט מהמשתמש .הקלט
יסתיים כאשר המשתמש יקיש על .Enterדוגמה לשימוש ב:raw_input-
קבצים
הטיפול בקבצים ב Python-פשוט בהרבה מזה שב .C-פונקציות הטיפול בקבצים מובנות ,ונקראות openו .close-פתיחת
קובץ נעשית באופן הבא:
כלומר ,הפונקציה openמחזירה אובייקט מסוג קובץ ,כאשר הפרמטרים לפונקציה הם שם הקובץ וסוג הגישה )’‘w
לכתיבה ‘r’ ,לקריאה ,וכו' כמו ב .(C-לאחר מכן ניתן לכתוב לקובץ באמצעות הפונקציה :write
)(>>> file.close
ניתן גם לפתוח קובץ לקריאה ולקרוא ממנו את כל התוכן שלו למשתנה אחד:
עמוד 21
Python 2.5
>>> file.close()
22 עמוד
Python 2.5
פרק _:6פונקציות
עד כה עסקנו במשחקים קטנים ונחמדים עם קוד קצר ופשוט .אבל ,כמו שנאמר במבוא Python ,היא שפה מאוד חזקה,
עד כדי כך שניתן לכתוב בה אפליקציות שלמות.
כמובן ,כמו בכל שפה ,כתיבת אפליקציות )או סתם תוכניות רגילות( תתבצע ע"י חלוקה לפונקציות ,פרוצדורות,
מודולים ,יחידות ספריה ,וכו' .פרק זה עוסק בפונקציות.
הגדרת פונקציות
בדומה ל ,C-אין הבדל בין פונקציות ) (Functionsלבין פרוצדורות ) – (Proceduresיש פונקציות שמחזירות ערך ויש
פונקציות שלא .בשונה מ ,C-פונקציה יכולה להחזיר ערך ,לא להחזיר כלום ,או להחזיר מספר טיפוסים שונים במקרים
שונים ,וזאת מבלי לשנות את ההגדרה שלה )ה Prototype-שלה(.
כמו כן ,חוקי ה Case Sensitivity-זהים לאלו של משתנים – יש הבדל בין אותיות גדולות וקטנות )לדוגמה ,פונקציה בשם f
איננה זהה לפונקציה בשם .(F
הגדרת פונקציה פשוטה )בלי פרמטרים(:
הפונקציה הזו לא מקבלת שום פרמטר ,וגם לא מחזירה כלום )אין בה משפט .(returnאם ניקח את ערך ההחזרה של
הפונקציה ונשים אותו בתוך משתנה ,המשתנה יכיל "כלום":
)(>>> h = func
’‘I am a function
>>> h
>>>
passבפונקציות
כמו בלולאות pass ,עובד יפה מאוד גם בפונקציות .פשוט רושמים passבמקום בלוק הפונקציה:
def doing_nothing():
pass
פרמטרים
העברת הפרמטרים לפונקציות ב Python-מאחדת למעשה את הנוחות של משתנים ב Python-עם אפשרות הDefault -
Valueשל .C++העברת פרמטרים תיעשה בסוגריים של הפונקציה:
לפונקציה זו העברנו שלושה פרמטרים ,b ,a :ו .c-כמו שניתן לראות ,אין צורך בטיפוס ,כי ב Python-משתנה יכול להיות
מכל טיפוס שהוא .כמובן ,זה לא אומר שניתן להעביר כל סוג משתנה שהוא לפונקציה ,הרי היא מצפה לסוג משתנה
מסוים .אם לא נוהגים בחופש זה בזהירות ,ניתן לגרום לכל תוכנית פשוטה לקרוס ממש מהר.
הפרמטרים שמועברים לפונקציה מועברים אליה ,By Valueכלומר נוצר עותק של המשתנה עבור הפונקציה ,והמשתנים
מצביעים לעותק זה .כמובן שעבור רצפים )רשימות וכו'( אין משמעות לעותק הנ"ל ,כיוון שהוא מצביע לאותו אובייקט.
בעת היציאה מהפונקציה ,יושמדו העותקים האלה )אלא אם כן הם יוחזרו ע"י הפונקציה ,ואז יכול להיווצר עותק עבור
ערך ההחזרה ,או שיוחזר העותק המקורי של הפונקציה – תלוי במימוש(.
ניתן לציין ערכי Defaultלפרמטרים .החוקים לזה דומים לאלה שב :C++-המשתנים בעלי ערך Defaultחייבים להופיע
כפרמטרים האחרונים )אי-אפשר שאחרי פרמטר בעל ערך Defaultיופיע פרמטר בלי ערך .(Defaultכמו כן ,בעת
הקריאה לפונקציה ,אי-אפשר להשאיר "פרמטרים ריקים" )כלומר ,לשים פסיק כדי לדלג על משתנה ,בניגוד ל,BASIC-
בה מותר לעשות דבר כזה( ,וחובה להעביר את כל הפרמטרים לפונקציה.
כמובן ,דוגמה:
עמוד 23
Python 2.5
)>>> func_with_defaults(1,,9
File “<stdin”>, line 1
)func(1,,9
SyntaxError: invalid syntax.
עם זאת ,נוכל "לדלג" על פרמטרים בעלי ערך מוגדר מראש ,ולהעביר רק את הפרמטרים שנרצה בצורה מפורשת:
עכשיו ,הדבר היחיד שחסר לנו כדי להשלים את כל התכונות של פונקציות ב C-ו C++-הוא ה) Ellipsis-כאשר שמים ...
בסוף שורת הארגומנטים לפונקציה כדי להעביר מספר לא ידוע של פרמטרים ,כמו ב .(printf()-דבר זה ממומש ב-
Pythonבצורה הרבה יפה מאשר ב :C-כאשר רוצים שפונקציה תקבל מספר לא ידוע של פרמטרים ,פשוט מוסיפים עוד
פרמטר אחד בסופה .לפני שם הפרמטר שמים כוכבית ,וזה אומר ל Python-לקחת את כל הפרמטרים שיתווספו אחרי
הפרמטרים הרגילים ולהפוך אותם ל .Tuple-אחרי זה ,העבודה מול Tupleהיא כבר נוחה ופשוטה.
בעיה קטנטנה שמתעוררת כאן היא כאשר יש לנו שתי פונקציות ,ושתיהן מקבלות " "Ellipsisכזה בסופן .מה יקרה כאשר
קראו לפונקציה אחת ,והיא רוצה להעביר את כל רשימת הפרמטרים לפונקציה השניה ,אבל לא כ ,Tuple-אלא כרשימה
אמיתית ,כאילו קראו לה עם רשימת הפרמטרים?
רק כדי להמחיש את הבעיה ,הנה שתי הפונקציות:
מה שקרה כאן הוא שבעת הקריאה ל Python ,yanti-המירה את הפרמטרים העודפים ל ,Tuple-אליו מצביע הפרמטר .y
כאשר yantiקראה ל ,parazi-היא העבירה )כפרמטר עודף אחד!( את כל ה ,Tuple-כי ה Tuple-הוא הרי טיפוס ,משתנה
אחד ,בדיוק כמו מספר שלם או מחרוזת.
paraziקיבלה את ה Tuple-כפרמטר אחד ,לקחה את הפרמטר והכניסה אותו ל Tuple-שלה ,אותו היא יוצרת עבור
הפרמטרים העודפים.
בקיצור ,בעוד ש parazi-ציפתה לקבל Tupleאחד ,היא קיבלה Tupleבתוך – Tupleסלט של -Tupleים ,וזה רק עם 2
פונקציות.
עמוד 24
Python 2.5
ברור שיש פה פתרון ,והוא אפילו ממש פשוט :בעת הקריאה ל parazi-צריך לשים כוכבית לפני .yהכוכבית אומרת ל-
Pythonלקחת את ה Tuple-הקיים כ Tuple-של הפרמטרים העודפים ,ולא ליצור Tupleחדש .הנה הפונקציות המתוקנות:
תיעוד
תיעוד )או באנגלית (Documentationהוא פעולה שאיננה כתיבת קוד .תיעוד הוא כתיבת הערות לתוכניות שלנו כדי
שאנחנו ,או אנשים אחרים ,נוכל לקרוא הערות על הקוד ולהבין אותו ,במקרה שהוא לא כל-כך מובן.
יש שני סוגים גדולים של תיעוד ב:Python-
.1סתם תיעוד שזורקים באמצע הקוד ,כדי שיהיה יותר ברור.
.2מחרוזת תיעוד קבועה בתחילת הפונקציה.
הסוג הראשון נעשה בצורה פשוטה מאוד – כמו שב C++-יש את רצף התווים " "//שאומר שמעכשיו ועד סוף השורה יש
דברים שלא צריך לקמפל )שכנראה יהיו הערות והארות( ,כך גם ב Python-יש את התו " "#שאומר שאין להתייחס למה
שכתוב מאחרי תו זה ואילך:
הסוג השני של תיעוד הוא סוג מיוחד ,ואליו Pythonמתייחסת בצורה שונה – בתחילת פונקציה ,שורה אחת אחרי שורת
ה) def-בלי שום רווחים מסתוריים( ,ניתן לשים מחרוזת )כמובן ,עם TABלפניה( .המחרוזת הזאת היא מחרוזת התיעוד של
הפונקציה .כאשר מישהו אחר )או אנחנו( ירצה יום אחד לדעת מהי הפונקציה הזו ,הוא יוכל לקבל את המחרוזת בקלות
רבה .העובדה שהתיעוד נמצא בתוך הקוד עצמו הוא דבר מאוד חזק – זה אומר שאם יש לך את הקוד ,יש לך את
התיעוד .אין צורך לחפש קובץ תיעוד שנעלם ,או יותר גרוע ,איזו חוברת שקבורה במגירה של מישהו אחר .מושג מחרוזות
התיעוד בתחילת פונקציות שאול משפת התכנות ,LISPוקרוי ) DocStringsאו (Documentation Strings
אז את מחרוזת התיעוד מקבלים ע"י כתיבת שם הפונקציה ,נקודה ,ו .__doc__ -ניתן גם להשתמש בפונקציה המובנית
)( helpשיודעת להדפיס בצורה יפה יותר מחרוזת תיעוד של פונקציה )או כל אובייקט אחר עם מחרוזת תיעוד(:
)>>> help(tor
__Help on function tor in module __main:
)(tor
I am a function that does absolutely nothing
אפשר גם להפעיל את )( helpעל מודול ולראות את רשימת כל הפונקציות שהוא מכיל ואת התיעוד של כל אחת מהן.
מחרוזות תיעוד כמו שראינו הן דבר נחמד .אבל ,ברוב הפעמים נרצה לכלול יותר משורה אחת בתיעוד ,שתכיל את מבנה
הפונקציה ,מה היא מחזירה ,פרמטרים ,תיאור מפורט ,וכו' .כדי לעשות את זה ,כותבים את מחרוזת התיעוד ,ומוסיפים
’-‘\nים כאשר רוצים לרדת שורה:
עמוד 25
Python 2.5
כדי להדפיס את מחרוזת התיעוד כמו שצריך )ולא את תוכן המחרוזת עצמה( ,נשתמש ב:print-
קצת יותר טוב ,אבל הדרך הנוחה ביותר שפיתון מציעה היא בעזרת סוג אחר של מחרוזות שנפרשות על פני כמה שורות:
למעשה ,כדי ליצור מחרוזת תיעוד בלי להסתבך עם -\nים מגעילים ,משתמשים במחרוזת עם 3גרשיים )כפולים או
בודדים ,העיקר שהגרשיים הפותחים והסוגרים יהיו סימטריים( .המחרוזת מסתיימת כאשר פיתון פוגשת שוב 3גרשיים,
וכל מה שבאמצע ,כולל -\nים ,נכנס למחרוזת:
__>>> f.__doc
'f(a, b, c) --> list\n\n Takes 3 integers (a, b, c), and returns a list with
those\n three numbers, and another number at the end of that list,\n which is
the largest number of all three.\n '
כמובן שמומלץ לבחון את המחרוזות עם )( helpאו printולא להסתכל עליהן ישירות.
משתנים בפונקציות
לקינוח פרק זה ,רק נכסה נושא אחד – מה קורה כאשר מכריזים על משתנים בתוך מקומות שונים בפונקציה.
המקרה הפשוט ביותר ,כאשר כותבים פונקציה ,ופתאום מכריזים בה על משתנה:
def func():
”print “Here we go
i = 9
while i > 0:
print i * 5
בפונקציה הזאת ,הוכרז משתנה בשם .iהמשתנה iיהיה קיים עד שהפונקציה תגיע לסופה ,ואז יושמד .אם נקרא
לפונקציות אחרות מתוך הפונקציה ,funcהן לא יכירו את המשתנה .iיותר מכך ,אם פונקציה אחרת תכריז על משתנה
באותו השם ,iלכ"א מהפונקציות יהיה משתנה iמשלה:
בנוסף לכך ,יכול להיות מקרה בו פונקציה יוצרת משתנה ,אבל המשתנה לא נוצר בתוך הבלוק של הפונקציה ,אלא
באחד מתת-הבלוקים שלה ,בתוך תנאי ifאו לולאת forאו .whileבמצב כזה ,המשתנה שנוצר יהיה תקף וקיים גם אחרי
היציאה מתת-הבלוק ,בלי שום קשר לזה שהוא נוצר בתוך הבלוק )בשונה מאוד מ Scopes-ב C-וב:(C++-
עמוד 26
Python 2.5
כאשר נשתמש בפונקציה ,נוכל לקבל ממנה מיד את שני הערכים לתוך שני משתנים:
וגם ,ברוב המקרים ברור לפיתון שאם ביקשנו מפונקציה להחזיר שני ערכים ,היא צריכה לעשות את זה ב .Tuple-לכן,
במקרים הברורים נוכל להשמיט את הסוגריים:
עמוד 27
Python 2.5
שלוש הפונקציות האלה הן פונקציות מובנות ב ,Python-שכל מטרתן היא לקחת רשימה )או ,(Tupleלהריץ פונקציה על
האיברים שלה )כ"א משלוש הפונקציות מריצה את הפונקציה בדרך שונה ,עם איבר או איברים שונים( ולהפיק פלט
מתאים.
כל זה נעשה ע"י קריאה לפונקציה אחת על הרשימה ,דבר שחוסך כתיבה מחדש של לולאה טריוויאלית בכל פעם
מחדש )ולפעמים קצת יותר מלולאה טריוויאלית(.
map
פונקצית קסם ראשונה.
mapמקבלת רשימה ופונקציה .הפונקציה חייבת לקבל פרמטר אחד בלבד map .מריצה את הפונקציה עם כ"א מאיברי
הרשימה )כל איבר בתורו ,לפי הסדר בו הם מופיעים ברשימה(.
mapמחזירה רשימה חדשה ,ובה כל איבר הוא התוצאה של הפונקציה עם האיבר המתאים לו ברשימה המקורית.
בעברית map :לוקחת את האיבר הראשון ,מריצה עליו את הפונקציה ,ושמה את התוצאה ברשימה החדשה .אח"כ היא
לוקחת את האיבר השני ,מריצה עליו את הפונקציה ,ושמה את התוצאה באיבר הבא ברשימה החדשה .כך היא עושה
לכל האיברים.
reduce
פונקצית קסם שניה.
reduceמקבלת רשימה ופונקציה .הפונקציה חייבת לקבל שני פרמטרים reduce .לוקחת את שני האיברים הראשונים
ברשימה ומריצה את הפונקציה כאשר האיבר הראשון הוא הפרמטר הראשון והאיבר השני הוא הפרמטר השני של
הפונקציה .לאחר מכן reduce ,לוקחת את התוצאה של הפונקציה ומריצה את הפונקציה עם התוצאה כפרמטר הראשון
והאיבר הבא מהרשימה כפרמטר השני .כך היא ממשיכה עד סוף הרשימה.
אם ברשימה יש רק איבר אחד reduce ,תחזיר את האיבר היחיד ברשימה )מבלי להריץ את הפונקציה( .אם הרשימה
ריקה reduce ,תקרוס ותדפיס הודעת שגיאה.
filter
פונקצית קסם שלישית.
filterמקבלת רשימה ופונקציה .הפונקציה חייבת לקבל פרמטר אחד filter .תיקח כ"א מאיברי הרשימה ותריץ את
הפונקציה עם האיבר הזה .אם התוצאה של ריצת הפונקציה היא ) Trueאיננה אפס ,איננה מחרוזת ריקה ,ולא הופכת
לאפס אם עושים לה Castingל ,(Integer-האיבר )האיבר מהרשימה שהועברה ל ,filter-לא ערך ההחזרה של הפונקציה(
יתווסף לרשימת התוצאה.
filterלמעשה מאפשרת לנו לסנן ערכים מרשימה נתונה ,בשורה אחת בלבד.
בדוגמה הזו הודפסו כל המספרים בין 0ל 19-ששארית החלוקה שלהם ב 2-איננה אפס .בקיצור ,כל המספרים האי-
זוגיים.
ניתן כמובן להפוך את התנאי ולהדפיס את כל המספרים הזוגיים:
עמוד 28
Python 2.5
lambda
אז מה lambdaעושה בכלל ולמה היא קשורה לפרק הזה?
כמו שאפשר לראות בדוגמאות למעלה ,עבור כ"א מההרצות של reduce ,mapאו filterהיינו צריכים לכתוב פונקציה
חדשה כדי שנוכל להעביר פונקציה לכ"א מהפונקציות .אבל כתיבת פונקציה חדשה כל פעם זה סתם העמסה על
הקוד ,ולפעמים גם יכול להיות מסובך למצוא את הפונקציה )אם שמנו אותה במקום אחר(.
lambdaמאפשרת לנו לחסוך עוד יותר בכמות הקוד – במקום לכתוב פונקציה חדשה בכל פעם lambda ,יוצרת עבורנו
פונקציה ללא-שם ,כבר בתוך שורת הקוד של reduce ,mapאו .filter
בת'כלס ,כאשר יוצרים פונקציה ,השם שנותנים לפונקציה הוא מצביע לפונקציה עצמה .זאת גם הסיבה שכאשר כותבים
שם של פונקציה ,בלי סוגריים Python ,כותבת לנו את שם הפונקציה .באופן הזה lambda ,יוצרת עבורנו פונקציה חדשה,
ומחזירה את המצביע לפונקציה החדשה .לפונקציה החדשה אין שם ,וכאשר תסתיים הרצת reduce ,mapאו ,filterגם
הפונקציה חסרת-השם תימחק.
דוגמה קטנה כדי להבין יותר טוב מה קורה:
קצת על המבנה של :lambdaכתיבת פונקצית lambdaלמעשה כולל רק את הפרמטרים לפונקציה ואת ערך ההחזרה
שלה ,כי למעשה אין צורך ביותר מכך )פונקציות lambdaהן ל"שימוש חד-פעמי"(.
והנה דוגמה לשימוש ב map-ו lambda-ביחד .הדוגמה עושה את מה שעושה הדוגמה הראשונה – מקבלת רשימת
מספרים ומחזירה רשימה של האיברים המקוריים כפול :2
בדוגמה הזו נכתבה פונקצית lambdaהמקבלת פרמטר אחד – – xומחזירה את .x*2כמובן ,אפשר לכתוב כל ביטוי
שתלוי ב ,x-ואפשר גם לכלול פונקציות בביטוי הזה.
def is_primary(n):
)))return reduce(lambda x, y: x and y, map(lambda x: n % x, range(2, n
def sum_of_digits(n):
))))return reduce(lambda x,y:x+y, map(int, map(None, str(n
עמוד 29
Python 2.5
מילה על יעילות
ישנה כמובן השאלה – מה יעיל יותר ,להשתמש בלולאה רגילה ) ,(for ,whileאו להשתמש )כשאפשר( בreduce ,map-
או ?filter
כנראה )עקב מימוש( יעיל יותר להשתמש ב reduce ,map-ו filter-במקום בלולאה רגילה בגלל שהפונקציות עצמן
ממומשות ב ,C-או בכל שפה עילית אחרת .היות והמימוש הוא בשפה נמוכה יותר מ ,Python-הריצה שלה תהיה מהירה
יותר.
מהכיוון ההפוך – כאשר מריצים לולאה רגילה ב ,Python-יש צורך באחזקת משתנה ) iלצורך הענין( ,או רשימה )range
לסוגיה( .האחזקה הזו לוקחת גם זיכרון וגם עיבוד נוסף )שנסתר ונחסך מהמתכנת( ,ולכן היא "יקרה" יותר.
השאלה הזו לא מוצגת כדי שנחשוב טוב טוב לפני שכותבים משהו ב Python-או ב ,C-אלא כדי להדגיש ששימוש
בפונקציות פנימיות הוא לרוב מהיר יותר .כמו כן ,לא צריך להשתגע ולכתוב תוכניות שלמות עם קריאות מסובכות ל-
reduce ,mapו.filter-
טוב להשתמש ב"קיצורי דרך" ,כל עוד הם גם משפרים את התוכנית ,הופכים אותה לקריאה יותר וקלה יותר ל.Debug-
עמוד 30
Python 2.5
פרק _:8מודולים
מהו מודול?
מודול הוא בסה"כ קובץ שמכיל קוד .Pythonלרוב ,לקובץ של מודול Pythonתהיה סיומת ,pyאך קיימות גם סיומות
אחרות ,לדוגמה dllאו ) soב Windows-או ,(UNIXשמציינות קובץ שמכיל פונקציות Pythonשכתובות ב) C-או באיזושהי
שפה מקומפלת אחרת( .לדוגמה ,המודול ) osאותו נכיר עוד מעט( כתוב ב.C-
כאשר קובץ הוא בעל סיומת Python ,pyיודעת שהקובץ מכיל טקסט רגיל ,ושיש להתייחס אליו כאל קוד .Pythonקוד
Pythonהוא כל דבר שהכרנו עד עכשיו – משתנים ,פונקציות ,שימושים במודולים אחרים ,if ,ולולאות .אם ניצור פונקציה
בקובץ כלשהו ,ונשמור אותו עם סיומת ,pyנוכל לטעון אותו ל Interpreter-או למודול אחר ולהשתמש בפונקציה
שכתבנו.
def func1(x):
))return map(lambda i: i * 2, xrange(0, x
def func2(x):
return 2 ** x
def func3(x):
return “The number is %d” % x
def func4(x):
return [x] * x
בקובץ זה 4פונקציות ,כאשר שם הקובץ בו הן מאוחסנות הוא .example.pyייבוא הקובץ לתוך התוכנית שלנו )או לצורך
הדוגמה ,ל (Interpreter-ייעשה באמצעות הפקודה .import
לפקודה importשתי צורות עיקריות ,כאשר כל-אחת מהן תשפיע בדרך מסוימת על האופן בו הפונקציות ייובאו למודול.
צורה ראשונה מאפשרת ייבוא כל הפונקציות מהמודול לתוך ה namespace-הגלובלי .המשמעות היא שכאשר נרצה
להשתמש בפונקציה מהמודול ,פשוט נכתוב את השם שלה.
צורה שניה היא ייבוא המודול ל namespace-משלו ,וכאשר נרצה להשתמש בפונקציה מהמודול ,נצטרך לרשום את שם
המודול ושם הפונקציה .צורה זו בטוחה יותר ודואגת שלא יהיו התנגשויות בין שמות פונקציות ממודולים שונים.
כאשר יש משתנים גלובליים מיוצאים ממודול ,תמיד יש לכתוב גם את שם המודול וגם את שם המשתנה בפנייה אליו,
בלי קשר לצורת ייבוא המודול.
לדוגמה ,הנה קוד שמייבא את examply.pyע"י הכנסתו ל namespace-משלו:
כפי שניתן לראות ,אין לכתוב את שם הקובץ כולו ,אלא להשמיט את הסיומת .pyכמו כן ,הפונקציות מיובאות לתוך
namespaceששמו הוא כשם המודול .אותו namespaceהוא בעצם אובייקט מסוג "מודול" ,כלומר אובייקט המדמה את
המודול כאשר אבריו הם השמות והאובייקטים המיוצאים ממנו.
הקוד הבא מייבא את example.pyל namespace-הגלובלי:
מבנה פקודת ה import-במקרה הזה הוא יותר מורכב .בדוגמה הזו בוצע ייבוא לכל הפונקציות במודול ,exampleולכן
הסימן * .ניתן לבצע ייבוא שכולל רק חלק מהפונקציות במודול .כמו כן ,הפונקציות יובאו כך שניתן לקרוא להן כאילו
הן הוגדרו הרגע ב) Interpreter-או בקוד בו עובדים(.
כאשר כותבים תוכניות גדולות ,קבצי Scriptוכד' ,מקובל לשים את פקודות ה import-בתחילת הקובץ ,בדומה לinclude-
ב.C-
דוגמה נוספת ל import-היא ייבוא של מספר פונקציות ספציפיות ממודול ,ולא את כל הפונקציות מהמודול:
עמוד 31
Python 2.5
ניתן לראות כי func1ו func2-יובאו מ ,example-אבל func3לא מוכרת כלל משום שצויין לייבא רק את func1ו.func2-
כדי לדעת אילו פונקציות קיימות במודול ,נוכל להשתמש בפונקציה המובנית )(:dir
)>>> dir(example
['__builtins__', '__doc__', '__file__', '__name__', 'func1', 'func2', 'func3',
]''func4
אפשר לראות שבעזרת )( dirאנחנו רואים את כל הפונקציות שיצרנו – .func1, func2, func3, func4למעשה )( dirמחזירה
רשימה של מחרוזות ,כאשר כל מחרוזת היא שם של אובייקט כלשהו בתוך המודול.
אנחנו יכולים לראות מהדוגמה שלמודולים יש עוד כמה Attributesנחמדים כמו __ __nameאו __:__file
__>>> example.__name
''example
__>>> example.__file
’‘example.py
כמובן שאם אנחנו משתמשים במודול exampleאנחנו כבר יודעים שקוראים לו ,exampleאבל __ __nameשימושי דווקא
מתוך המודול ,למשל אם נרצה לכתוב קוד שלא תלוי בשם של המודול ,נוכל להשתמש ב __name__-כפרמטר.
__ __docהוא ה Documentation String-של המודול ,בדומה למחרוזת תיעוד של פונקציות .כדי ליצור מחרוזת תיעוד
למודול ,פשוט נשים מחרוזת בתחילת המודול ,לפני פונקציות או importים אחרים:
import os
def func1(x):
))return map(lambda i: i * 2, xrange(0, x
def func2(x):
return 2 ** x
def func3(x):
return "The number is %d" % x
def func4(x):
return [x] * x
את המחרוזת הזו נוכל אח"כ לראות ע"י גישה ל __doc__-או בעזרת )(.help
)print “You have a dog named %s and a cat named %s” % (dog_name, cat_name
עמוד 32
Python 2.5
לפני שנמשיך נציין שמקובל לקרוא לקבצי פיתון שנועדו להרצה ) Scriptאו תסריט ,סקריפט( .אין כמובן הבדל בין קובץ
פיתון לקובץ פיתון אחר מבחינת פיתון .ההבדל היחיד הוא בצורת השימוש.
עכשיו ,בהנחה ש Python-מותקנת על המחשב ,נוכל להריץ את התוכנית שכתבנו .לדוגמה ,נעשה זאת מ:cmd-
כלומר כדי להריץ את quick_script.pyמשורת הפקודה ,אנחנו צריכים לציין שאנחנו רוצים להריץ את Python.exe
מהספרייה בה הוא מותקן )המיקום הדיפולטי הוא ,C:\Python25עבור גרסה 2.5כמובן( ולאחר מכן לתת את שם
הסקריפט.
כאשר מתקינים את פיתון על Windowsאפשר גם להריץ את הסקריפט ע"י .Double-Clickהחסרון היחיד הוא שאם יש
שגיאה בהרצת הסקריפט ,לא נוכל לראות אותה כי החלון של פיתון ייסגר.
#!/usr/bin/python
)print “You have a dog named %s and a cat named %s” % (dog_name, cat_name
~$ ./quick_script.py
?what’s your cat name
bush
?what’s your dog name
charly
!you have a dog named charly and a cat named bush
באופן כללי ,מאוד נוח לכתוב -Scriptים שרצים ב Unix-כמו תוכניות רגילות.
#!/usr/bin/python
import sys
print sys.argv
ונריץ:
וכמו שאפשר לראות ,הפרמטרים לתוכנית ,כולל שם התוכנית עצמה ,נכנסים לרשימה ב.sys.argv-
עמוד 33
Python 2.5
המודול os
מודול זה מכיל פונקציות לתקשורת עם מערכת ההפעלה .המודול לא תלוי במערכת ההפעלה עליה מורצת תוכנית ה-
.Pythonקיימות הספריות ntו posix-לתקשורת עם מערכות הפעלה Windows-NTו-UNIX-ים למיניהם ,אבל osמכילה
פונקציות ומשתנים שיעבדו על כל מערכת.
כדי להשתמש במודול נכתוב:
>>> os.name
’‘nt
המשתנה יכיל את סוג מערכת ההפעלה ,ולא שם מלא כמו " "Windows 95וכד' .כאשר Pythonמורצת על מערכת ,Java
os.nameיכיל את המחרוזת ’.‘java
המשתנה environבמודול הוא מילון המכיל את משתני הסביבה של המערכת )כמו המיקום של ספריית ה.(temp-
משתני הסביבה משתנים ממערכת למערכת ,אך ניתן לראות את כל המשתנים הללו ע"י קריאה ל keys()-של המילון:
)(>>> os.environ.keys
[‘WINBOOTDIR', 'PATH', 'BLASTER', 'PATHEXT', 'TEMP', 'COMSPEC', 'PROMPT', 'WINDIR',
]’'TMP
הפונקציה )( systemבמודול מאפשרת להריץ פקודות של ה Command Line-ולקבל את הפלט שלהן בערך ההחזרה של
הפונקציה .שימוש נחמד יכול להריץ את הפקודה :dir
Directory of z:\x
הפונקציה למעשה מחזירה ערך ,בדוגמה זהו ,0ובנוסף לכך מודפס למשך כל מה שהפקודה שלחה להדפסה למסך.
זאת הסיבה שהשורה האחרונה היא – 0זו לא השורה האחרונה ,אלא ערך ההחזרה של הרצת הפקודה.
הפונקציה )( mkdirו rmdir()-יוצרות ומוחקות ספריות ,אבל ההבדל בין שימוש ב sysetm()-כדי לשגר פקודת מערכת לבין
השימוש בכ"א מהפונקציות הללו הוא שהפונקציות מתאימות לכל מערכת-הפעלה ,והשימוש ב system()-מצריך יצירת
פקודה שונה לכל מערכת.
עמוד 34
Python 2.5
המודול sys
המודול sysמכיל את כל הפונקציות שקשורות למערכת ה Python-ולהרצת תוכנית ה .Python-המודול כולל בתוכו
פונקציות לטיפול בקלט ופלט )לסוגיו השונים( ,מידע על גירסת ה ,Interpreter-משתנים שמשפיעים על התצוגה ,מידע
על המודולים הטעונים ,סדר הבתים )אינדיאניות( ,גודל ה ,int-ועוד.
כמה משתנים חשובים שיש במודול הם – versionמכיל את גירסת ה Interpreter-עליה מורצת התוכנית– platform ,
מכיל את סוג מערכת ההפעלה עליה רצה התוכנית )בניגוד ל ,os.name-מכיל רק את סוג מערכת ההפעלה ,למשל
’ – path ,(‘win32מכיל את כל הספריות בהן ייעשה חיפוש בעת טעינת מודולים ,ו – maxint-מכיל את גבול משתנה ה.int-
כמו כן ,המודול מכיל שלושה משתנים – stdout ,stdinו .stderr-אלו הם אובייקטי הקבצים של הקלט הסטנדרטי )לרוב
המקלדת( ,הפלט הסטנדרטי )לרוב המסך( ופלט השגיאות הסטנדרטי )המסך או קובץ( .ניתן לכתוב ולקרוא מקבצים
אלה )כל קובץ וההרשאות שלו – ברור לדוגמה שאי-אפשר לקרוא מ (stdout-לשימושים מיוחדים .הפונקציות printו-
raw_inputמשתמשות באובייקטים אלה.
כאשר מריצים -scriptים מה Command Line-ניתן לתת להם ארגומנטים ,ממש כמו לכל תוכנית רגילה .ב C-לפונקציה
)( mainהיו שני פרמטרים – argcו ,argv-בהם הועברו ארגומנטים אלה .ב Python-קיימת רשימה בשם argvבתוך .sys
הרשימה מכילה את הארגומנטים ,לפי הסדר בו הם הועברו ,כאשר הארגומנט הראשון הוא לרוב שם הקובץ המורץ .אם
ה script-שרץ כרגע )או לצורך העניין ה (Interpreter-לא הורץ משורת הפקודה argv ,היא רשימה ריקה.
המודול string
מודול זה מכיל ,בנוסף לפונקציות שיכולות לפעול על מחרוזות שכבר הכרנו בפרק על טיפוסי נתונים ,גם כמה מחרוזות
קבועות שמכילות אוספים של תווים שימושיים – uppercaseמכילה את כל האותיות הגדולות lowercase .מכילה את כל
האותיות הקטנות digits .מכילה את כל הספרות ) 0עד hexdigits .(9מכילה את כל הספרות בבסיס 0) 16עד .(F
octdigitsמכילה את כל הספרות בבסיס 0) 8עד whitespace .(7מכילה את כל התווים שנחשבים כרווח )תו רווח ,טאב,
מעבר שורה וכו'( punctuation .מכילה את כל תווי הניקוד.
המודול math
מודול זה מכיל מספר פונקציות שימושיות וקבועים לשימושים מתמטיים ,כמו חישובים עם πאו ,℮או משחקים משונים
עם מספרי נקודה-עשרונית.
piו e-הם שני קבועים המייצגים את πואת ℮בדיוק של 11ספרות אחרי הנקודה.
הפונקציה ceilמקבלת מספר עשרוני ומעגלת אותו כלפי מעלה floor .מקבלת מספר ומעגלת אותו כלפי מטה )מקצצת
את החלק העשרוני(.
הפונקציות cos ,sinו tan-הן כמובן שלוש הפונקציות הטריגונומטריות האהובות ,ו acos ,asin-ו atan-הן הפונקציות
ההופכיות להן.
שאר הפונקציות מ math-די טריוויאליות ,וניתן פשוט לרפרף עליהן ע"י הסתכלות במחרוזת התיעוד )__ (__docשל כ"א
מהן.
המודול time
בלי הרבה הפתעות ,המודול timeמכיל פונקציות לטיפול בזמן .הפונקציה הפשוטה ביותר שקיימת במודול זה היא
)( ,timeשמחזירה את הזמן הנוכחי .הזמן מוחזר במשתנה מסוג ,floatוהוא מייצג את מספר השניות שעברו מאז 1.1.1970
בחצות.
פונקציה שימושית נוספת במודול היא )( ,sleepשמקבלת מספר שניות כ float-ומשהה את התוכנית לפרק הזמן הזה.
שאר הפונקציות במודול משמשות לטיפול ולהדפסה של זמנים בצורות נוחות לבני אדם וכן לטיפול ב.Time Zones-
המודול random
מודול זה מכיל פונקציות ליצירה של מספרים אקראיים .הפונקציה שהכי דומה לפונקציות מקבילות מ C-היא )(,random
שמחזירה מספר אקראי מסוג floatבין 0.0ל.1.0-
אבל זה לא נוח במיוחד ...בכלל זה הוסיפו עוד פונקציות נוחות במיוחד ,לדוגמה )( ,randrangeשמחזירה מספר אקראי
בתחום מסוים:
>>> import random
)>>> random.randrange(3, 90
47
וכן גם יש פונקציה בשם )( shuffleשמקבלת רשימה ומבלגנת את הסדר של האיברים שלה.
עמוד 35
Python 2.5
בתכנות מבני היו לנו מודולים .מודול בדרך-כלל הכיל איזשהו טיפוס נתונים מופשט ,כאשר מטרת המודול הייתה
לאפשר למשתמש ליצור ולטפל בטיפוס זה .דוגמה למודול כזה הוא מודול רשימה ,כאשר יש טיפוס של רשימה,
ופונקציות שונות )אתחול רשימה ,הרס רשימה ,הוספת איבר ,חיפוש איבר ,וכד'( .לרוב ,כדי שלפונקציות יהיו שמות
משמעותיים וכדי שלא יהיו התנגשויות בין שמות ממודולים שונים ,לכל פונקציה היינו מוסיפים קידומת של שם המודול
)במקרה של רשימה הפונקציות היו נקראת ,list_add ,list_initוכו'(.
בתכנות מונחה עצמים יש לנו עצמים .עצם בעולם התכנות הוא חיה שמייצגת איזשהו עצם במציאות .הכוונה במייצג
היא שהעצם מכיל בתוכו מידע שמתאר עצם מסוים )בין אם העצם מוחשי או מופשט( ,ובדומה לטיפוס הנתונים
המופשט מהמודול ,העצם מכיל בתוכו פעולות אותן ניתן להפעיל על העצם .המידע שמאפיין את העצם נקרא תכונות
) ,(Propertiesוהפעולות שניתן להפעיל על העצם נקראות מתודות ) .(Methodsכאשר יוצרים עותק של עצם )כלומר
מקום בזיכרון בו יאוחסנו התכונות של עצם מסוים( ,הדבר נקרא ) Instanceמופע בעברית( Instance .הוא ייצוג של
עצם בודד בזיכרון.
כמו כן ,לעצם יש מתודות מיוחדות שנקראות בעת הבניה ) (Constructionוההריסה ) (Destructionשל העצם .המתודה
שנקראת בעת הבניה מכונה בנאי ) ,Constructorאו בקיצור ,(Ctorוהמתודה שנקראת בעת ההריסה מכונה הורס
) ,Destructorאו בקיצור .(Dtor
PythonוOOP-
Pythonהיא שפה מונחית-אובייקטים .זה אומר ש Python-תומכת בהגדרת ויצירת עצמים ,וכן תומכת בירושה
ופולימורפיזם )בצורה מסוימת( .כל המושגים האלה יובהרו בהמשך.
בנוסף ,ב Python-כל דבר הוא אובייקט ) .(Everything is an objectכל מה שלמדנו עד עכשיו – מספרים ,מחרוזות,
רשימות ,מילונים ,ספריות ומודולים ,קבצים ,וכו' – כל אלה מיוצגים ע"י עצמים .כאשר משתמשים בפונקציה ,type
למעשה מקבלים את שם העצם:
)>>> type(0
>’<type ‘int
)’>>> type(‘abc
>’<type ‘str
כאשר יוצרים מופעים חדשים של מחלקות type ,מחזיר את הסוג ’ ‘instanceעבורם .בהמשך נראה למה זה ככה ומה
עושים עם זה.
הגדרת מחלקה
הגדרת מחלקה )תיאור של עצם( נעשית ע"י שימוש במילת המפתח :class
טוב ,אז הדבר המוזר הזה זאת הגדרה של מחלקה חדשה .שם המחלקה הוא .Aבתוך המחלקה הכרזנו על מתודה בשם
.funcהמתודה מקבלת פרמטר אחד בשם ) selfמיד נראה בשביל מה צריך אותו( ומדפיסה הודעה למסך.
הפרמטר selfהוא פרמטר שחייב להיות הפרמטר הראשון בכל מתודה במחלקה self .הוא למעשה משתנה המכיל את
ה Instance-עליו תורץ הפונקציה ,כי אם נריץ "סתם פונקציה" ,היא לא יכולה לדעת על איזה מופע עליה לפעול – כמו
שבתכנות מבני יכולות להיות כמה רשימות באותה התוכנית ,כך בתכנות מונחה עצמים יכולים להיות כמה מופעים של
אותו העצם .ב C++-הדבר נעשה באמצעות .this
אם כן ,לכל מתודה במחלקה מועבר הפרמטר ,selfוהמשתמש יכול לשנות ולהוסיף תכונות למחלקה )דרך אגב,
הפרמטר הנ"ל לא חייב להיקרא ,selfאך זה הרגל מקובל ומושרש בעולם הפיתוני .(...דבר זה נעשה בדיוק כמו עם
משתנים רגילים ,כלומר השמת ערך למשתנה שאינו קיים תיצור אותו ותאתחל אותו לערך זה:
המתודה func1מאתחלת משתנה בשם prop1להיות prop1 .17הוא מאפיין במופע selfשל .Aהמתודה func2מדפיסה
את ערך המאפיין prop1למסך.
עמוד 36
Python 2.5
בדיוק כאן מתעוררת בעיה – מה יקרה אם המשתמש במחלקה יפעיל את המתודה func2לפני – func1הרי אם func1
לא הופעלה prop1 ,לא נוצר ,אז מה func2תדפיס? במקרה כזה תהיה שגיאה ,משום ש prop1-אכן לא קיים ,והפנייה
אליו תהיה כמו פנייה למשתנה שלא נוצר מעולם.
בדיוק בשביל זה קיימות פונקציות הבנייה וההריסה של מחלקה .פונקציות אלה ) Constructorו (Destructor-נועדו לרוץ
בעת יצירת המחלקה ובעת הרס המחלקה .יצירת המחלקה תהיה ברגע שהמשתמש יוצר מופע חדש של המחלקה –
כלומר מובטח שפונקצית האתחול תרוץ לפני כל פונקציה אחרת במחלקה .באותו האופן ,כאשר מופע עומד להיהרס
)אם הוא היה משתנה זמני בפונקציה והיא הסתיימה ,אם המשתמש מחק את ה Instance-עם ,delוכו'( תורץ פונקצית
ההריסה ,שתאפשר לבצע פעולות סיום למיניהן.
יש לציין שפונקציות הבנייה וההריסה לא נועדו רק לייצור משתנים פנימיים של מחלקה ,אלא עבור אתחול מחלקה,
קריאה לפונקציות בנייה שונות ,אתחול עצמים פנימיים ,וכו'.
ב ,Python-פונקצית היצירה נקראת __) __initשני קווים תחתונים לפני ואחרי המילה (initופונקצית ההריסה נקראת
__ .__delהנה דוגמה למחלקה Aאחרי הוספת פונקצית יצירה:
וכעת ,כאשר ייווצר מופע של ,Aקודם כל תורץ הפונקציה __ ,__initללא כל צורך לקרוא לה בצורה חיצונית .בצורה
דומה ,ניתן להוסיף מתודה בשם __ __delשתיקרא בעת הריסת מופע של :A
יש לשים לב שב ,Pythonלעתים קשה לחזות מתי בדיוק אובייקט ייהרס Python .היא שפה המכונה בעגה המקצועית
" ,"Garbage-Collectedמה שאומר שהיא אחראית על הריסת אובייקטים בזמן המתאים ביותר להריסתם ,מה שעלול
להיות אחרי פקודת ה delאו אחרי הפסקת השימוש באובייקט.
בנוסף ,יש להיזהר בעת השימוש בפונקציית __ – __delלרוב אין צורך בה ,אבל אם משתמשים בה יש לוודא שבעת
השמדת המחלקה ,לא תהיה פנייה למחלקות אחרות שכבר הושמדו )שכן רוב הפניות לפונקציית ההשמדה יהיו בעת
סיום תוכנית ,שלב בו לפעמים לא ברור מהו סדר ההשמדה של עצמים(.
בדוגמה הגדרנו מחלקה בשם ,Aכאשר פונקצית היצירה שלה מקבלת 3פרמטרים – selfאותו חובה לקבל בכל מתודה,
nameו .age-פונקצית היצירה יוצרת את self.my_nameואת self.my_ageומכניסה לתוכם את הערכים הנ"ל .כמו כן,
המחלקה כוללת שתי מתודות לקבלת nameו.age-
יצירת המחלקה מתבצעת בשורה האחרונה ,כאשר משתנה בשם personמקבל את ה Instance-של המחלקה החדשה
שנוצרת.
עמוד 37
Python 2.5
המחלקה נוצרת ע"י כתיבת שם המחלקה ,Aואחריה סוגריים .בסוגריים נרשמים כל הפרמטרים שיועברו לאחר מכן ל-
__ ,__initלפי הסדר ,ולאחר הפרמטר .selfכמובן שאין צורך לרשום את ,selfהוא נמצא רק במימוש הפנימי של
המחלקה ,ואינו מוכר מחוץ אליה.
)(>>> person.get_name
’‘moshe
)(>>> person.get_age
17
>>> person.my_age
17
>>> person.my_name
’‘moshe
כפי שניתן לראות ,הפעלת מתודה של ה Instance-היא כתיבת שם המשתנה המכיל את ה ,Instance-נקודה ,שם
המתודה ,וסוגריים הכוללים את הפרמטרים למתודה )בדוגמה אין שום פרמטרים למתודות(.
כמו כן ,המאפיינים my_ageו my_name-של המחלקה זמינים בדיוק כמו ב struct-של .Cמרגע שהם נוצרו בתוך
המחלקה ,ניתן לקרוא ולשנות אותם גם מחוץ למחלקה .בהמשך נראה כיצד מחביאים מאפיינים של מחלקה כך שיהיו
זמינים רק למתודות של המחלקה.
בדוגמה האחרונה ראינו כיצד ניתן ליצור מופעים של מחלקות ולהריץ מתודות שלהם – מספקים משתנה המכיל את ה-
,Instanceוקוראים למתודה שנמצאת בתוכו .דרך נוספת להריץ מתודה של מחלקה על מופע מסוים היא קריאה ישירה
למתודה של המחלקה )כלומר ,שימוש בשם המחלקה ולא בשם ה ,(Instance-תוך העברת פרמטר המכיל מופע:
)>>> A.get_name(person
’‘moshe
בדרך זו העברנו למעשה את personכפרמטר selfבצורה מפורשת .דרך קריאה זו אמנם פחות יפה ,אבל יש לה
שימושים ,בעיקר כאשר יש צורך להשתמש ב reduce ,map-או .filterלדוגמה ,ניצור רשימה ובה 5מופעים של .Aלאחר
מכן ניצור רשימה ובה רק שדות ה my_name-של המחלקה:
])>>> people = [A(‘moshe’, 17), A(‘haim’, 34), A(‘igor’, 51), A(‘amir’, 21), A(‘x’, 68
)>>> map(A.get_name, people
]’[‘moshe’, ‘haim’, ‘igor’, ‘amir’, ‘x
לגבי מתודות בתוך מחלקה ,אין הגבלות על קריאות של מתודות אחת לשניה ,רקורסיה של מתודה וכד' .בנוסף ,היתרון
ב Python-הוא שמתודה אחת יכולה לקרוא למתודה אחרת שעדיין לא הוגדרה .דבר זה אפשרי בגלל שהרצת המתודה
תתבצע ע"י ה Interpreter-רק לאחר שכל הפונקציות כבר הוגדרו ,ולכן הפונקציה שהוגדרה "מאוחר יותר" תהיה זמינה
)ב C-היה צורך לשנות את סדר הפונקציות או ליצור -prototypeים לפונקציות(:
עמוד 38
Python 2.5
Pythonמספקת מנגנון להחבאת משתנים פנימיים ומתודות פנימיות של המחלקה כך שהמתכנת מציין אילו מתודות
ומאפיינים הוא מעוניין להחביא ,ו Python-משנה את השם המקורי של כ"א מהאיברים להחבאה לשם אחר .כאשר נעשה
שימוש באחד מהשמות ה"מוחבאים" Python ,פונה לשם האחר.
השמות האחרים הללו אמנם נגישים מבחוץ ,אבל הם לא קבועים ויכולים להשתנות ממימוש למימוש ,ולכן לא ניתן
לפנות אליהם בצורה חד-משמעית .דבר זה מבטיח הגנה מסוימת מטעויות שעלולות להתבצע בשוגג .מצד שני ,אם
משתמש מעונין לשנות משתנים פנימיים של מחלקה מסוימת ,אין שום קושי לבצע dirעל מחלקה ולראות אילו משתנים
קיימים בתוכה )ואולי להבין מי מהם הוא המשתנה שהוא מחפש(.
החבאת משתנים או מתודות נעשית ע"י הכנסת שני קווים תחתונים לפני השם שלהם .בכל פנייה למשתנים או מתודות
אלו יש לציין את השם עם הקווים התחתונים )הם חלק מהשם ,ולא תוספת חד-פעמית שמציינת החבאה(:
כפי שניתן לראות ,לא ניתן היה לגשת למאפיין __aשל ,Cכי הוא מוחבא ,אבל ניתן בהחלט לראות את הערך שלו ע"י
שימוש במתודה get_aשיכולה לגשת למאפיין )כי היא בתוך המחלקה(.
למעשה ,ההחבאה אינה החבאה מוחלטת ,אלא הוספת שם המחלקה ,בתוספת מספר קווים-תחתונים ,לשם המשתנה.
בצורה הזו לא ניתן לגשת למשתנה בצורה טריוויאלית )צריך "לעבוד קצת" כדי להשיג את המשתנה(.
מטרת ה"החבאה" היא לא החבאה מושלמת – מטרתה היא להבהיר שמשתנים מוחבאים הם משתנים פנימיים שלא
מומלץ לגעת בהם.
דוגמה לשם משתנה לאחר ההחבאה שלו:
כפי שניתן לראות ,בתוך aיש משתנה בשם ,_A__xוהוא למעשה המשתנה הפנימי .__x
ירושה
עד עכשיו ראינו כיצד ניתן ליצור מחלקות ,להגדיר מתודות למחלקות אלה ,וליצור מופעים של מחלקות .כל אלה
מאפשרים לאגד את כל התכונות והפעולות של עצם מסוים בטיפוס אחד ולגשת אליו בצורה נוחה.
אבל ,מה קורה אם נרצה לקחת עצם קיים ולהרחיב אותו? כלומר ,לקחת מחלקה ולהוסיף לה מספר מתודות ,כדי
להפוך אותה לעצם "משוכלל יותר" .דרך אפשרית היא לפתוח את קוד המקור של המחלקה ולשנות את המחלקה
עצמה כך שתכיל את כל המתודות החדשות שנרצה בנוסף לאלו שכבר קיימות .אמנם בדרך זו נקבל את התוצאה
עמוד 39
Python 2.5
הרצויה ,אבל שכפול קוד קיים אינו דבר טוב – אם יום אחד נרצה לשנות את המחלקה המקורית ,ניאלץ לבצע את אותם
השינויים גם בכל המחלקות שהעתקנו )אם אנחנו רוצים את השינוי( .אם מדובר בבאג במחלקה המקורית ,שינויים
ושיפורים כבר הופכים למסובכים בהרבה.
הפתרון לכל הבעיות האלה הוא ירושה ) .(Inheritanceירושה היא לקיחת מחלקה קיימת ,ושימוש בה ליצירת מחלקה
חדשה .ההבדל בין ירושה לבין הפתרון הקודם לבעיית השימוש במחלקה קיימת הוא שבירושה אין העתקה של קוד,
אלא שימוש בקוד קיים.
בירושה יש מחלקת אב ) (Parent Classומחלקת בן ) .(Sub Classמחלקת האב היא המחלקה ממנה יורשים ,כלומר
המחלקה ממנה לוקחים את כל המאפיינים והמתודות הקיימות .מחלקת הבן היא המחלקה החדשה שנוצרת ממחלקת
האב וכוללת את המתודות והמאפיינים הנוספים למחלקת האב.
כאשר מגדירים את מחלקת הבן ,אין צורך לממש )ואפילו אין צורך להגדיר( את המתודות ממחלקת האב – מעצם
העובדה שמחלקת הבן יורשת ממחלקת האב ,כל המתודות של מחלקת האב מובאות לתוך מחלקת הבן.
המחלקה מייצגת חיה ,כאשר החיה יכולה לגדול ,לאכול ,לשחק ,ללכת לשירותים ולישון.
הקוד הבא מגדיר מחלקה בשם ,Dogשיורשת מ Animal-ומכילה מתודות חדשות:
המחלקה Dogיורשת ממחלקת ,Animalובנוסף לכל המתודות ש Animal-מגדירה Dog ,מכילה 2מתודות חדשות –
מתודה לנביחה ומתודה לכשכוש בזנב.
יש לשים לב שבמתודת ה __init__-של Dogיש קריאה למתודת ה __init__-של .Animalדבר זה נעשה כדי לאפשר ל-
Animalלאתחל את כל המאפיינים שקשורים אליו .קריאה זו לא נעשית אוטומטית )שלא כמו ב ,(C++-ויש לציין אותה
במפורש.
שימוש במחלקה החדשה Dogהוא בדיוק כמו שימוש במחלקה רגילה:
בנוסף להגדרת מתודות נוספות במחלקה )כאלה שמרחיבות את המחלקה הקיימת( ,ניתן ליצור מתודות שמחליפות
מתודות במחלקת האב .כלומר ,אם נרצה נוכל ליצור מתודה במחלקה ,Dogכך שכאשר עובדים עם מופעים של ,Dog
במקום לקרוא לפונקציה מסוימת ב ,Animal-קוראים לפונקציה אחרת ב.Dog-
עמוד 40
Python 2.5
דבר זה נעשה ע"י יצירת פונקציה בעלת אותו שם כמו ב ,Animal-שתהיה ממומשת ב .Dog-לדוגמה ,הנה הגדרה אחרת
של ,Dogכאשר יש בה החלפה של המתודה growמ Animal-במתודה חדשה של עצמים מסוג :Dog
כעת ,כאשר נקרא למתודה ,growתיקרא המתודה מ ,Dog-ולא מ :Animal-סדר החיפוש של מתודות הוא מהמחלקה
ה"חדשה" ביותר ,כלומר Pythonתחפש קודם את המתודה במחלקת הבן ,ורק אחר כך במחלקת האב .אם יש שרשרת
של ירושות )כלומר ,מחלקת האב היא מחלקת בן בעצמה( ,החיפוש יתבצע שוב ושוב עד שלא יהיה איפה לחפש או עד
שתימצא מתודה בשם המבוקש.
נושא נוסף שקשור לירושה הוא ירושה מרובה .נושא זה לא יילמד במסגרת חוברת זו.
בנוסף ,המודולים שמסופקים עם Pythonמכילים אף הם הרבה מאוד מחלקות מוכנות לשימוש .מחלקות אלה יכולות
לחסוף הרבה זמן בשימוש בהן ,או בירושה מהן כדי ליצור מחלקה מותאמת אישית .מומלץ להיעזר בתיעוד ולסקור את
המחלקות הקיימות.
עמוד 41
Python 2.5
דמיינו לעצמם שהתוכנית שלכם יודעת לבד מתי יש שגיאה ,ואתם יכולים להגיד לתוכנית שתבצע רצף פעולות מסוים,
כאשר אתם גם אומרים לה מה לעשות אם תהיה שגיאה .בצורה כזו ,ניתן לכתוב תוכניות הרבה יותר פשוטות ,כי אין
צורך לבדוק כל הזמן ערכי החזרה מפונקציות ואין צורך לטפל בכל שגיאה בנפרד – התוכנית יודעת לבד מתי יש
שגיאה ,והיא יודעת מה לעשות אם יש שגיאה.
זוהי בדיוק דרך הפעולה של ) Exceptionsחריגות בעברית( .ב Exceptions-המשתמש מגדיר קטעי קוד ,אותם הוא תוחם
בצורה מיוחדת כדי שניתן יהיה לדעת בדיוק על איזה קטע קוד עובדים עכשיו .בנוסף ,המשתמש מגדיר לכל קטע קוד
כזה קטע של טיפול בשגיאות – זהו קוד נוסף שיתבצע אם תהיה שגיאה .ניתן לכתוב כמה קטעי קוד לטיפול בשגיאות,
אחד לכל סוג שגיאה משלו.
ב ,Python-כאשר יוצרים קוד ששיטת הטיפול בשגיאות בו היא ע"י ,Exceptionsכותבים קטעי קוד לניסיון בבלוקים
שנקראים .tryמיד אחרי כל בלוק ,tryיופיע לפחות בלוק exceptאחד .בלוק exceptהוא בלוק התפיסה ,כאשר הוא
כולל פרמטר המציין את סוג השגיאה )מיד נראה מה זה סוג שגיאה( .זריקה מתבצעת ע"י הפקודה ,raiseשמקבלת
כפרמטר את סוג השגיאה שיש לזרוק.
לדוגמה ,קטע הקוד הבא מתבצע עד שפקודת ה raise-מתרחשת .ברגע שמדיעים לפקודת ה ,raise-השליטה עוברת
לבלוק ה except-המתאים:
ואכן ,השגיאה שנזרקה הייתה מסוג ,Error2וקטע ה except-המתאים התבצע .יש לציין שכמו שהתבצעה זריקה מתוך
מקטע ה ,try-כך גם יכולה להתבצע זריקה מתוך פונקציות שקטע ה try-קרא לה ,או מתוך פונקציה שהפונקציה שקטע
ה try-קרא לה ,וכו' .כלומר ,אי-אפשר לדעת מאיפה בתוך הקטע תיזרק השגיאה.
אז מה זאת השגיאה ,Error2או ליתר דיוק מה זה אומר שגיאה מסוג ?Error2התשובה דווקא פשוט – שגיאה היא כל
אובייקט שיורש מהמחלקה .Exception
המחלקה Exceptionהיא מחלקה מובנית בשפה ,וכל הException-ים חייבים לרשת ממנה .קיימים כל-מיני Exceptionים
שכבר ראינו לאורך החוברת ,אך לא התייחסנו אליהם ,כמו SyntaxErrorאו .TypeErrorשני Exceptionים אלה ,ועוד
הרבה אחרים ,הם Exceptionים מובנים ב Python-שנזרקים עלינו בתגובה לדברים רעים שאנחנו עושים בשפה או
במנגנונים מובנים שלה.
עמוד 42
Python 2.5
Pythonמאפשרת לנו ליצור Exceptionים חדשים משלנו ,כדי שנוכל לציין במפורש שגיאות שלנו ,מבלי להשתמש
בשגיאות קיימות .כך נוכל לוודא שאנחנו תופסים ומטפלים בשגיאה הנכונה ,אם היא תיזרק.
לדוגמה ,ניצור Exceptionחדש ונשתמש בו בפונקציה )(:f
אם נרצה לתפוס את השגיאה שיצרנו ,נוכל לעטוף את הקריאה ל f()-בבלוק :try…except
>>> try:
... )f(3
... except BadThingHappened:
... 'print 'everything is ok
...
everything is ok
הדוגמה האחרונה הייתה פשוטה ,אך כמובן חסרת כל שימוש .כעת נראה דוגמה לשימוש אמיתי בException-ים ,ולצורך
כך נכתוב פונקציה שמוחקת קובץ .הפונקציה תקרא ל ,os.remove-ואם תקרה שגיאה )במקרה של os.removeשגיאה
יכולה להיות שהקובץ לא קיים או שאסור למחוק אותו( נתעלם ממנה:
import os
def delete_file(path):
try:
)os.remove(path
except OSError:
pass
יש לציין ש Python-מחפשת את קטע הקוד לטיפול בשגיאה לפי הסדר בו קטעי ה except-כתובים .דבר זה אומר שאם
קטע exceptאחד תופס סוג אחד של שגיאה ,והוא בא לפני קטע exceptאחר Python ,תנסה קודם להתאים את ה-
Exceptionשנזרקה לסוג השגיאה הראשון ,ורק לאחר מכן לסוג השגיאה שבא אחריו ,וכן הלאה לכל קטעי ה:except-
בדוגמה הזו ניתן לראות ש SpecificFailure-יורש מ ,GeneralFailure-אך התוכנית שלנו מנסה לתפוס את השגיאות בסדר
כזה שבו היא מגיעה למסקנה שהשגיאה שנזרקה היא .GeneralFailureאמנם השגיאה היא גם מסוג ,GeneralFailureאך
ברור שלא נגיע לקטע הקוד שמטפל ב SpecificException-בצורה כזו.
כדי לתפוס את SpecificExceptionקודם ,יש לשים את בלוק ה except-של SpecificExceptionלפני זה של
.GeneralException
עמוד 43
Python 2.5
למה זה טוב?
אמנם מנגנון ה Exceptions-נראה קצת מסורבל בהתחלה ,אבל Exceptionsמאפשרים לכתוב קוד קצר בהרבה מקוד
"רגיל" שצריך לבדוק לאחר כל קריאה לפונקציה או פעולה על משתנה .בגישה ה ,Exceptions-אין צורך לבדוק ערך
החזרה מפונקציה ,מפני שאם פונקציה צריכה להחזיר שגיאה ,היא תזרוק ,Exceptionבמקום להחזיר ערך אחר.
בנוסף ,שיטת ה Exceptions-מאפשרת להגדיר סוג חדש של שגיאה מבלי לשנות קוד קיים – כל מה שצריך זה להוסיף
עוד קטע .exceptלעומת זאת ,בשיטה הישנה ,היינו צריכים להוסיף עוד בדיקה לכל קריאה לפונקציה שיכולה להחזיר
את השגיאה החדשה.
למה זה רע?
למרות כל היתרונות )שעדיין לא כולם ברורים בשלב זה של הפרק( של ה ,Exceptions-יש להם חסרון משמעותי :כאשר
נזרק Exceptionוהוא לא נתפס ,הוא ימשיך לפעפע בניסיון למצוא בלוק exceptשיתפוס את השגיאה .אם לא יימצא כזה
בלוק ,התוכנית תקרוס .לפעמים אנחנו מעונינים בקריסת התוכנית ,ולפעמים התוכנית לא יכולה להמשיך לפעול וחייבים
לסיים אותה ,אבל אי-תפיסת Exceptionשנזרקה תגרום לסיום "מכוער" של התוכנית – כלומר הדפסה של כל
הפונקציות שנקראו בדרך ,עם עוד המון זבל שמשתמש רגיל לא יבין בכלל .לכן יש לדאוג לסיום נקי של תוכנית ,כלומר
תפיסת כל Exceptionשהוא ,וטיפול מתאים בו.
הבעיה המרכזית בכתיבת קוד שמשתמש במנגנון ה Exceptions-היא ווידוא שהקוד בטוח – תופס את כל הExceptions-
האפשריים.
עמוד 44
Python 2.5
פרק _:12תרגילים
פרק זה כולל תרגילים מסכמים לחוברת זו.
מעצם היותם מסכמים ,התרגילים לא קצרים – כלומר לוקח לא מעט זמן לכתוב אותם בפעם הראשונה ,ובטח עם שפה
חדשה ,ובעיקר בהתחשב בעובדה שההכרה עם השפה היא לא עמוקה ודרושה קריאה נוספת בתיעוד ומעבר נוסף על
החומר.
בנוסף ,בגלל שמטרת התרגילים היא לתרגל את שפת ,Pythonולא עיצוב תוכנה ,לכל תרגיל יש המלצות בסופו לגבי
דרך המימוש המומלצת .כמובן שאין חובה לפתור את התרגילים לפי הדרך המומלצת.
ולסיום – תשקיעו! גם אם הכל נראה ברור ,לנסות לפתור תרגיל אחד לא יזיק...
סתם תרגיל
בתרגיל זה עליכם לכתוב תוכנית )בכל צורה שרק בא לכם( שתקבל שם קובץ ,תפתח את הקובץ ותוסיף בתחילת כל
שורה את השם שלכם ובסוף כל שורה את שם המשפחה שלכם .בסיום התוכנית עליכם לשמור את הקובץ המעודכן
בקובץ חדש.
המלצות:
בצעו dirעל אובייקט הקובץ הפתוח כדי לראות מה הוא מציע. •
קראו בתיעוד על הפונקציה readlinesשל אובייקט קובץ. •
כספומט
עליכם לכתוב תוכנית שתדמה מכשיר כספומט ) ATMבאנגלית( .התוכנית תקרא ,בעת העליה שלה ,קובץ המכיל את
כל לקוחות הבנק ,את מספרי החשבונות שלהם ,את סיסמת הגישה לכספומט שלהם ואת היתרה בחשבונות שלהם.
יתרה יכולה להיות שלילית.
את מבנה הקובץ בו יישמרו הלקוחות אתם קובעים ,ועליכם ליצור קובץ כזה בעצמכם לפני הפעלת התוכנית.
התפריט של לקוח בבנק יכיל את האפשרויות הבאות:
.1בירור יתרה
.2משיכת מזומנים
.3הפקדת מזומנים
.4שינוי סיסמא
בסיום הרצת הכספומט )ע"י הזנת חשבון מספר ,(-1יישמר קובץ הלקוחות ,כאשר הוא מכיל את המצב המעודכן של
היתרות והסיסמאות של כל הלקוחות.
המלצות:
ליצור קובץ לקוחות כטקסט פשוט ,כאשר כל שורה מייצגת לקוח .בכל שורה יופיעו שם הלקוח ,מספר •
החשבון שלו ,הסיסמא והיתרה שלו .כ"א מהנתונים יופרדו ברווחים או בפסיקים.
אחסון הלקוחות במילון ,כאשר המפתח הוא מספר החשבון ,וכל איבר הוא רשימה בת שלושה איברים •
המכילה את שם הלקוח ,הסיסמא והיתרה שלו.
כאשר משתמש מקיש מספר חשבון ,ניתן לדעת מיד האם החשבון קיים ע"י שימוש בפונקציה has_keyשל •
המילון.
יומן פגישות
המטרה בתרגיל היא ליצור תוכנית שמאחסנת אירועים שמתרחשים בתאריכים ובשעות שונים ,ולהציג את כל האירועים
שעומדים להתרחש היום.
המשתמש בתוכנה יוכל להכניס אירועים חדשים ,כאשר הוא מציין את:
שם האירוע •
מקום האירוע •
תאריך האירוע •
שעת האירוע •
התוכנית צריכה לאפשר למשתמש לבקש את האירועים שעומדים להתרחש היום ולהדפיס את כל האירועים האלה .אם
משתמש מזין אירוע חדש ,והאירוע החדש נמצא בתאריך ושעה בהם כבר קיים אירוע אחר ,התוכנית תקבל את האירוע
החדש ,ויהיו שני אירועים באותו הזמן.
התוכנית לא צריכה לשמור אירועים שכבר קרו בעבר ,ויש לדאוג לניקוי של אירועים שכבר התרחשו.
המלצות:
עיינו במודול timeובפונקציות שלו. •
אחסון האירועים השונים ייעשה ברשימה ,כאשר כל איבר שלה הוא מחלקה שבה שדות לאחסון כל המידע •
על אירוע.
בטבלה הזו ,העוצמה קובעת אילו פעולות חזקות יותר מאחרות – למשל ,חיבור חזק יותר מכפל )כי כפל בא לפני
חיבור( ,ושלילה באה לפני כולם.
פעולות הממוצע ,מינימום ומקסימום מקבלות שני אופרנדים ,ומבצעות עליהם את הממוצע ,מקסימום ומינימום
בהתאמה .למשל:
2 @ 4צריך להחזיר ,3כי זהו הממוצע בין 2ל.4- •
7 $ 2צריך להחזיר ,7כי 7גבוה מ.2- •
כאשר מופיעות מספר פעולות אחת אחרי השניה ,ולכל הפעולות עוצמה זהה ,יש לבצע את הפעולות בסדר בו הן
מופיעות .כמו כן ,המחשבון צריך לכלול טיפול בסוגריים )רק סוגריים מרובעות( ,כאשר סוגריים קודמות לכל.
המלצות:
לפני פענוח המחרוזת ,להסיר את כל הרווחים שהמשתמש הוסיף – אין להם משמעות בביטוי. •
פענוח המחרוזת ייעשה בצורה הדרגתית ,כאשר בכל פעם סורקים את המחרוזת מחדש כדי לחפש את •
הפעולה החזקה ביותר .כאשר מוצאים פעולה חזקה ,מבצעים אותה .אם אין את הפעולה החזקה ביותר ,מנסים
למצוא פעולה חלשה יותר.
בכל מעבר על הביטוי ,פותרים את הפעולות החזקות ביותר ,ויוצרים ביטוי חדש פשוט יותר .את הביטוי הזה •
שולחים שוב לפתרון )רקורסיה(.
כאשר אין יותר פעולות לבצע ,סימן שהביטוי פתור. •
עמוד 46