תוצאות החיפוש
66 items found for ""
- פונקציות - functions
פונקציות - functions טוב מכאן מתחיל להיות ממש מגניב (מילה משנות ה- 80 שאין לוותר עליה ובטח שלא להחליף אותה במילה קול). במקום להשתמש רק בפונקציות שפייתון נותנת לנו, אנו יכולים ליצור פונקציות משלנו שמוגדרות על פי מילים שאנו בוחרים, ולהשתמש בפונקציות שיצרנו בהמשך לצורך בניית תוכנית מורכבת יותר אך מובנת וקריאה (readable) יותר. פונקציה יוצרים על ידי שימוש במילה def שהיא קיצור של definition (הגדרה), הבה ניצור פונקציה ונלמד להשתמש בה. def power (n): return (n**2) print (power(3)) >>> 9 print(power(4)) >>> 16 הסבר לתוכנית למעלה (הכוללת את שתי השורות הראשונות) המייצרת פונקציה המקבלת משתנה n ומחזירה את ערך המשתנה בריבוע. לאחר המילה def בחרנו שם לפונקציה שלנו (power) ובחרנו גם שם של משתנה (n). לאחר מכן נקודתיים (:), שורה מתחת מוזחת ימינה אנו רושמים מה הפונקציה שלנו תעשה עם הערך שיוזן ב- n לפני שתחזיר (return) אלינו את התוצאה. וזהו סיימנו יצרנו פונקציה בשם Power, אבל כדי שהפונקציה תעשה משהו, צריך להשתמש בה, או במילים אחרות לקרוא לה. הקריאה לפונקציה או השימוש בפונקציה נעשה באמצעות שימוש בשם הפונקציה והזנת ערך במקום n. כך קיבלנו פונקציה פשוטה שלוקחת את הערך 3 למשל ומחזירה 9 או לוקחת את הערך 4 ומחזירה 16. אפשר גם פונקציות בלי פרמטרים - def printush (): for i in range(3): print("sababa") printush() >>> sababa sababa sababa בתוכנית למעלה כדאי לשים לב – שלאחר שיצרנו את הפונקציה printush (בשלוש שורות קוד) היא לא מדפיסה שום דבר עד שלא קוראים לה. הקריאה להפעלת הפונקציה מתבצעת באמצעות שם הפונקציה וסוגריים עגולים ריקים printush() לאחר הקריאה לפונקציה אנו לא צריכים לבקש print כי פעולת ההדפסה מוכלת בפונקציה שכתבנו. הפונקציה מכילה לולאת for שעוברת על טווח המספרים מ- 0 עד 3 (לא כולל 3) ובכל פעם מדפיסה את המילה sababa. משתנה שנוצר בפונקציה נשאר בפונקציה – כאשר אנו יוצרים משתנים בתוך פונקציות, זה לא אומר שהאות או המילה שהשתמשנו בה בוזבזה לנצח, ניתן לראות אותם כמשתנים זמניים וניתנים לשימוש חוזר הן מחוץ לפונקציה והן בלולאות אחרות בתוך הפונקציה – i=5 def printush (): i=3 print("sababa"*i) printush() >>> sababasababasababa print(i) >>> 5 תחילה הגדרנו את המשתנה i והכנסנו לתוכו את הערך 5. לאחר מכן, לצורך הפונקציה השתמשנו באותה אות i והכנסנו בה את הערך 3. כאשר ביקשנו מחוץ לפונקציה את הערך של i , קיבלנו את הערך שהיה לפני שכתבנו את הפונקציה. לעיתים נרצה שמשתנה מסויים שנמצא מחוץ לפונקציה, יחול גם בתוך הפונקציה, לשם כך קיימת הפקודה global המגדירה משתנים כחלים על טווחים רחבים יותר. i=0 def printush (): global i i=3 print("sababa"*i) printush() >>> sababasababasababa print(i) >>> 3 בתוכנית למעלה אנו רואים שכדי שהמשתנה הכללי i יושפע ממה שקורה בתוך הפונקציה, הוספנו את הפקודה global לתוך הפונקציה, וכעת כל שינוי שנערך ב- i בתוך הפונקציה, יחול הלאה גם מחוצה לה. פונקציה עם מספר פרמטרים אפשר גם לכתוב פונקציה הכוללת כמה פרמטרים - def mul (a,b): return (a*b) print(mul(5,6)) >>> 30 הפונקציה למעלה נקראת mul והיא מקבלת שני פרמטרים a ו- b ומחזירה את המכפלה של שניהם. היות שבפונקציה אין הוראה מובנית להדפיס אזי לא מספיק לקרוא לפונקציה, אלא צריך גם לבקש להדפיס את מה שהפונקציה מחזירה. במקרה הזה הכנסנו את 5 ו- 6 וקיבלנו 30. אפשר גם להחליט שפרמטרים מסוימים יכילו ערכים כברירת מחדל (שאין לא הכנסנו כלום בפונקציה היא תשתמש בברירת המחדל) def mul (a,b=2): return (a*b) print(mul(5,6)) >>> 30 print(mul(7)) >>> 14 בדוגמא למעלה אנו יכולים לראות שאם אנו נכניס לפונקציה שני פרמטרים היא תיתן מכפלה שלהם כמו קודם. אבל אם נכניס פרמטר אחד בלבד, היא תשתמש בברירת המחדל לפרמטר השני (המספר 2) – כך שאם נכניס את המספר 7 כפרמטר ראשון ויחיד נקבל את התוצאה 14. פונקציה עם מספר בלתי מוגדר של פרמטרים - *args – כאשר הפונקציה שלנו צריכה לקבל בוודאות שני פרמטרים ואולי גם מספר בלתי מוגבל של פרמטרים נוספים, היא יכולה להראות כך - def mul(a, b,*args): m=a*b for n in args: m=n*m return m print(mul(2,3,4,5)) >>> 120 :אפשר יותר חכם גם ככה def mul(*args): m=1 for n in args: m*=n return m print(mul(2,3,4,5,6)) >>> 720 בפונקציה שלמעלה המבצעת מכפלה של פרמטרים, המשתמש חייב להזין לפחות שני פרמטרים (a ו- b ) אחרת מה הקטע של מכפלה ? אבל יכול לבחור להזין גם יותר (ככל העולה על רוחו). לשם כך אנו מגדירים משתנה כללי (מילה שבחרנו במקרה הזה *args) אשר מתחילה בכוכבית. לא לבלבל עם כוכבית של כפל, כאן המשמעות שונה. משתנה כללי המתחיל בכוכבית בעצם מייצג כמות בלתי מוגבלת של משתנים נוספים (או כלום), אשר הלולאה for שבפונקציה מגדירה מה לעשות איתם. והיא קובעת שכל מספר שנזין בפונקציה חוץ מהשניים הראשונים, יוכפל גם הוא בתוצאת המכפלה של שני הפרמטרים הראשונים. לבסוף אנו מבקשים מהפונקציה להחזיר את הערך שהתקבל במשתנה הזמני m שיצרנו לשם כך, ואשר יכלול בסוף התהליך את מכפלת כל המספרים שהעברנו בפונקציה 2*3*4*5 שווים ל- 120. המילה *args עם הכוכבית היא מוסכמה בפייתון, מהמילה ארגומנט (argument). אפשר כל מילה אחרת - עם כוכבית- רצוי כזאת המתארת בדייקנות רבה יותר את מהות הפרמטרים הנוספים הללו. אפשר להשתמש גם לצורך הכנסת פרמטר מילולי (מחרוזת) - def names(a, b,*args): m=a+" "+b for n in args: m= m+" "+n return m print(names("eddie","arbili", "the","author")) >>> eddie arbili the author פרמטרים מילוליים – kwargs** – לעיתים נרצה להזין בפונקציה פרמטרים מילוליים כמילון, לשם כך הומצא **kwargs מהביטוי keyword argument , מה שחשוב לתוכנה זה שתי הכוכביות, את המילה kwargs אפשר לשנות למשהו עם משמעות ברורה יותר בהתאם לתוכנית שבונים. השימושים, כמובן , כמו במילון, ונדגים – def dic(**kwargs): for k,v in kwargs.items(): if v<=27: print(f"{k} is {v} years old - just a kid") elif v > 27: print(f"{k} is {v} years old - an adult") dic(moshe=25,baruch=49,nitzan=19) >>> moshe is 25 years old - just a kid baruch is 49 years old - an adult nitzan is 19 years old - just a kid
- תרגיל – מספרי פיבונאצ'י
תרגיל – מספרי פיבונאצ'י מספרי פיבונאצ'י הם סדרה של מספרים אשר כל מספר בה הוא סכום של שני המספרים שקדמו לו (מתחילים במספרים 0 ו- 1 – לפעמים מתחילים ב- 1 וממשיכים משם)– הנה למטה 7 מספרים בסדרת פיבונאצ'י משמאל לימין - 0,1,1,2,3,5,8 כל מספר הוא סכום השניים הקודמים לו בסדרה. המשימה שלנו היא לבנות פונקציה עם פרמטר מספרי אחד (נניח n) שיגיד מה גודל הסדרה של פיבונאצ'י שאנו רוצים ליצור, ואשר תייצר את הסדרה באופן אוטומטי עבורנו. כך למשל אם נגיד שאנו רוצים סדרה בגודל 7 איברים, היא תפיק את הרשימה שהבאנו בדוגמא למעלה מ- 0 עד 8. כמובן שיש דרכים רבות לבנות תוכנית שכזאת, אנו נציג את אחת הדרכים הבסיסיות יותר – def fib(n): i=0 a=0 b=1 while i<n: print (a) i +=1 c=a+b a=b b=c fib (10) >>> 0 1 1 2 3 5 8 13 21 34 השלבים – בחרנו שם לפונקציה – fib וגם שם לפרמטר המגדיר את גודל הסדרה n הגדרנו שלושה משתנים נוספים: i שישמש אינדקס לסיום הלולאה כאשר הסדרה תגיע לגודל שהגדרנו, הערך ב-i מתחיל ב- 0 והוא גדל באחד עם כל סיבוב של הלולאה. וכן שני תאים נוספים a ו- b אשר יכילו תמיד את שני הערכים האחרונים שחישבנו בסדרה. אנו מזינים ב a ו- b את שני הערכים הראשוניים בסדרת פיבונאצ'י (0,1) ומתחילים לייצר את הלולאה. הלולאה שבחרנו היא מסוג while ויש בה תנאי מפסיק (שעוצר את הלולאה) והוא כאשר i יהיה שווה ל- n שהוא מספר האיברים שאנו רוצים לכלול בסדרה. כלומר מובטח לנו שהלולאה תרוץ מספר פעמים השווה ל- n (זה נראה כי מספר הפעמים שהלולאה רצה אינו שווה ל- n משום שהלולאה אינה ממשיכה פעם נוספת כאשר i=n , אבל מכוון שמתחילים מ- 0 ולא מ- 1 אנו מקבלים את מספר האיברים בסדרה כפי שביקשנו, כך למשל אם אנו מבקשים את כל המספרים הקטנים מ-3 – אנו מקבלים שלושה מספרים 0,1,2). הלולאה שלנו בנויה כך שבכל פעם התא a יכיל את המספר הבא בסדרה, הוא מתחיל מ- 0 כי כך הגדרנו. לאחר פעולת ההדפסה אנו לוקחים את סכום שני המספרים שיש בתאים a ו- b ושומרים אותם באופן זמני במשתנה שקראנו לו c. לאחר מכן אנו מחליפים את הערכים ב- a ו- b . לתוך a אנו מכניסים את הערך שהיה ב- b ולתוך b את הערך שהיה ב- c כך שבסיבוב הבא של הלולאה a יכיל את המספר הבא בסדרה ו- b את זה שאחריו. ונראה את זה בטבלה – מספר הסיבובים בלולאה הערך של a הערך של b ערך התחלתי 0 1 לאחר סיבוב 1 1 1 לאחר סיבוב 2 1 2 לאחר סיבוב 3 2 3 לאחר סיבוב 4 3 5 לאחר סיבוב 5 5 8 אנו רואים שהערכים של a מהווים את סדרת פיבונאצ'י והערכים של b הם הערך הבא בתור. לאחר שכתבנו את הפונקציה, הפעלנו אותה, או במילים מקצועיות קראנו לה - (fib(10 - והכנסנו את המספר 10 בפרמטר, כך שהתוצאה שקיבלנו היא עשרת מספרי הפיבונאצ'י הראשונים מ- 0 עד 34.
- תרגיל – פלינדרום
תרגיל – פלינדרום פאלינדרום (אפשר גם בלי א' או בכלל באנגלית palindrome) הוא מספר הוא מילה (אפשר גם משפט אבל זה מורכב יותר) שאפשר לקרוא מהסוף להתחלה והוא יישאר אותו דבר. למשל המילה שמש, גם אם קוראים מהסוף להתחלה זה עדיין שמש. כך גם המספר 121 או 1001. המשימה שלנו היא לבנות פונקציה המקבלת פרמטר (מילה או מספר) ומחזירה תשובה האם הוא פלינדרום או לא. יאללה def pali (p): sp=str (p) rp=sp [::-1] if sp==rp: print ("palindrome") else: print ("not palindrome") pali ("שמש") >>> palindrome pali ("ana") >>> palindrome pali ("פוף") >>> not palindrome השלבים – הגדרנו פונקציה בשם def עם פרמטר p. הגדרנו משתנה בשם sp שלוקח את מה שנכניס בפרמטר p והופך אותו למחרוזת משום שהערך המספרי , גם אם מכניסים מספר, לא ממש מעניין אותנו, אלא רק סדר הסימנים. הגדרנו משתנה rp שלוקח את המחרוזת sp והופך ורושם אותה מחדש מהסוף להתחלה (קפיצות של -1 משמעותן שעוברים על המחרוזת כולה [::] החל מהאיבר שמספרו 1- (האחרון) וכל פעם מוסיפים 1- כך שהאיבר הבא יהיה 2- (השני מהסוף) וכו'. אנו רואים שמחרוזת מתנהגת כמו רשימה. אפשר לחתוך אותה או להפוך אותה. עכשיו, לאחר שהפכנו את המחרוזת מהסוף להתחלה אנו יכולים להשוות אותה (תנאי של if) למחרוזת המקורית, כך שאם הן שוות אזי מדובר בפלינדרום. אבל רגע, יש לנו bug, גם המילה פוף היא סוג של פלינדרום, למה קיבלנו not palindrome ? זאת משום שפייתון לא מזהה ש פ' סופית (ף) היא גם סוג של פ'. לכן עכשיו נתקן את התוכנית – def poli(p): sp=str(p) if "ף" in sp: np=sp.replace("ף","פ") sp=np rp=sp[::-1] if sp==rp: print ("palindrome") else: print ("not palindrome") poli ("פוק קוף") >>> palindrome עכשיו לאחר יצירת המחרוזת sp אנו בודקים אם יש בה ף (פ' סופית) ובמידה שכן, אנו עושים שימוש בפקודה שפועלת על מחרוזות ומאפשרת יצירת מחרוזת חדשה np שבה תוחלף כל ף בפ' רגילה. לאחר מכן במקרה שקיימת ף אנו מבקשים להכניס לתוך sp את המחרוזת המתוקנת ומשם ממשיכים כמו בתוכנית הקודמת, יוצרים משתנה rp שהופך את המחרוזת ומשווה אותה למחרוזת המקורית לאחר החלפת ף (פ' סופית) ב - פ' רגילה. כמובן שניתן לבצע את התהליך על כל הסופיות ובדרכים יעילות יותר. באנגלית צריך להתמודד עם אותיות גדולות מול קטנות כשהופכים את כיוון הקריאה. ולא, לא מצאתי משהו טוב יותר מ- "פוק קוף" יש לי גם "פושטק קטשופ" אבל זה עוד אחד מאותו ז'אנר.
- פונקציית lambda
פונקציית lambda פונקציות מיועדות לשימוש חוזר בתוכנית, לעיתים מספר פעמים, או לבניית מבנים מורכבים יותר. אולם לעיתים אנו רוצים משהו לשימוש קצר וחד פעמי שנכתב במשפט אחד. הפונקציה גם נקראת פונקציה אנונימית משום שאין לה שם. למה זה טוב ?! ממציא התוכנה חושב היום שזה לא טוב למרות שזה חוסך שורות קוד, זה הופך את התוכנה לפחות קריאה וברורה אבל זה מסורתי והוא לא הצליח להוריד את זה מהגרסאות המתקדמות רק בגלל לחץ המשתמשים, זה מזכיר שם של ריקוד, אז נלמד את זה. עוד הערה לפני כן, בהמשך נלמד את הנושא של list comprehension שמאפשר ליצור מבנים מהירים שגם הוא מייתר במקרים רבים את ה- lambda אבל מסורות זו מסורת, סבבה?! נכתוב תוכנית הסוכמת ערך של שני מספרים שאנו מצבים, עם פונקציה רגילה ועם lambda ולאחר מכן נסביר - תוכנית עם פונקציה רגילה- def sum(a,b): return a+b print(sum(5,8)) >>> 13 תוכנית עם פונקציית lambda - sum=lambda a,b : a+b print(sum(5,8)) >>>13 בשני המקרים מתקבלת התוצאה 13 ואופן השימוש בפונקציה הוא זהה, אבל ב- lambda חסכנו שורה אחת של קוד. אנו רואים שלמבדה לוקחת את המשתנים מצד שמאל של הנקודתיים ומחזירה ערך בהתאם לפעולה הנדרשת בצד ימין של הנקודתיים, בלי גינוני נימוסין בלי return ובלי def בהתחלה. תוכנית המחזירה פונקציה (lambda) - def multifunc(n): return lambda x: x * n f=multifunc(10) print(f(5)) >>> 50 print(f(12)) >>> 120 התוכנית למעלה נקראת multifunc היא מאפשרת ייצור מהיר של פונקציה שתיקח את הערך של n ותוכל לקבל את המכפלה שלו בכל מספר שהמשתמש יבחר. לפונקציה שאנו מייצרים באופן הזה קראנו f והבסיס אותו היא מכפילה בכל פעם הוא 10 כך ש (f(5 נותן את הערך 50 ו- (f(12 את הערך 120. מספיק עם הקשקוש הזה.
- List comprehension –רשימה מהירה
List comprehension –רשימה מהירה קודם הדרך המסורתית ליצירת רשימה – lista=[ ] for i in range(10): lista.append(i) print(lista) >>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] והדרך המקוצרת list comprehension– lista=[i for i in range(10)] print (lista) >>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] פשוט מכניסים לתוך סוגריים מרובעים (כך אנו יודעים שמדובר ברשימה) את הנוסחה למה שאנו רוצים שיופיע ברשימה. במקרה שלנו אנו מפעילים את הלולאה for בשילוב עם פקודת range ומבקשים שהרשימה תכלול את מה שיהיה בתוך המשתנה i עבור כל i בטווח שקבענו. יש לנו שורה אחת במקום 3 ורמת הקריאות היא גבוהה יותר מאשר בפונקציית lambda המסורתית עאלק. אפשר גם עם פקודת list הלוקחת ישירות מהטווח שמייצרת פקודת range ומייצרת רשימה - print (list(range(5)) >>> [0, 1, 2, 3, 4] ועוד כמה אפשרויות נאות – lista=[x*x for x in range(1,6)] print (lista) >>> [1, 4, 9, 16, 25] lista=[x*x for x in range(1,11) if x%2!=0] print (lista) >>> [1, 9, 25, 49, 81] למעלה- התוכנית הראשונה מייצרת רשימה של ריבוע כל המספרים מ - 1 עד 5 (לא כולל 6) התוכנית השנייה כוללת, באותה שורה, גם תנאי (if) ששארית המנה של כל אחד מריבועי המספרים (כשהוא מחולק במספר 2) בטווח (1-10 כולל 10 לא כולל 11) תהיה שונה (!=) מ- 0 – כלומר רק מספרים אי זוגיים. והכל בשורה אחת של קוד (לא מחשיב את שורת print בחשבון הזה) .
- תרגיל list comprehension איתור BMI חריג
תרגיל list comprehension איתור BMI חריג המטרה שלנו היא לכתוב פונקציה, בשורה אחת, המפיקה רשימה של משקלים בין 70-80 ק"ג ורשימה של גבהים בין 160-180 ס"מ ומוצאת רק את הצירוף של משקל וגובה אשר מדד ה-BMI שלו עולה על 30 (וכנראה צריך דיאטה) – הנוסחה למדד BMI משקל לחלק ל- (גובה בריבוע) - הגובה בתצורה של 1.75 מטר. התוכנית print([f"{w}kg:{h}cm" for w in range(70,81) for h in range(160,181) if w/(h/100)**2>30 ]) התוצאה ['77kg:160cm', '78kg:160cm', '78kg:161cm', '79kg:160cm', '79kg:161cm', '79kg:162cm', '80kg:160cm', '80kg:161cm', '80kg:162cm', '80kg:163cm'] בתוכנית למעלה כתבנו בשורה אחת, באמצעות list comprehension שמשמעותה- נא להפיק רשימה, והיא מבוצעת באמצעות פתיחה וסגירה של סוגריים מרובעים (זה הכל). בתוך הסוגריים המרובעים השתמשנו ב- format שעזר לנו לשרטט את תצורת הצגת נתוני המשקל והגובה הגדולים מ- 30 זאת עושים באמצעות האות f לפני הציטוט של המחרוזת, כאשר בתוך המחרוזת שתולים משתנים בתוך סוגריים מסולסלים. בנוסף ישנן שתי לולאות for הפועלות להפקת רשימות הגובה והמשקל וכן תנאי if המגביל את מה שאנו רוצים לקבל. הכל בשורה אחת של קוד.
- פונקציה רקורסיבית
פונקציה רקורסיבית לעיתים אפשר להימנע מלהשתמש בפונקציה רקורסיבית, ואפשר לעשות את העבודה בדרכים מעט ארוכות יותר, אבל קריאות ומובנות יותר. מצד שני, אין דבר שעושה רושם גדול יותר על המראיינים שלכם בעתיד או בכלל מאשר לנסח פונקציה רקורסיבית קצרה שעושה את העבודה ומדהימה את הלקוח. חוץ מזה אנחנו בעניין פיתוח חשיבה אנליטית, וגם באנו ליהנות. קודם נגיד את זה - מדובר בפונקציה שמחזירה מלבד ערך כלשהו גם את עצמה באופן מעגלי עד לסיום עבודתה. לנוכח מורכבות העניין, חבל להכביר מילים ונתאר בדוגמא. פיבונאצ'י - fibonacci בפונקציה רקורסיבית הדוגמא הראשונה תהיה אותה סדרת פיבונאצ'י שכתבנו לגביה תוכנית בתרגיל למעלה, (0,1,1,2,3,5,8…) שכל איבר שווה לסכום שני האיברים שלפניו בסדרה. אלא שהפעם נבנה את הדברים מעט אחרת, באמצעות פונקציה רקורסיבית שמחזירה את האיבר ה- n (למשל אם n==7 התוכנית תחזיר את המספר 8 שהוא האיבר השביעי). כמובן ש n לא יכול להיות 0 בתוכנית הזאת, אחרת תוחזר שגיאה. def fib(n): if n==1: return 0 elif n==2: return 1 else: return fib(n-1)+fib(n-2) for i in range(1,11): print (fib(i)) >>> 0 1 1 2 3 5 8 13 21 34 ניתן לראות בתוכנית המגדירה את הפונקציה fib שנעשה שימוש בפונקציה fib, גם בתוך הגדרת הפונקציה עצמה ! (מסתבר שאפשר לעשות את זה). אם n שווה ל- 1 מדובר באיבר הראשון בסדרה ולכן התוכנית מחזירה 0 ואינה ממשיכה לרוץ משום ששרשרת התנאים (או בלוק התנאים) if,elif,else היא אלטרנטיבית, כלומר אם מתקיים תנאי באחד מהחלקים בשרשרת, לא ממשיכים לתנאים הבאים בשרשרת ובמקרה הזה הלולאה מסתיימת. כנ"ל אם n שווה ל- 2 אנו נלכדים בתנאי השני elif והתוכנית מחזירה את המספר 1 שהוא האיבר השני בסדרה שלנו ובכך מסתיימת הלולאה ואנו יודעים שהאיבר השני בסדרת פיבונאצ'י שלנו הוא 1. אם אנו מחפשים את האיבר השלישי (n==3) בסדרה, אנו לא נלכדים בהתחלה בתנאי הראשון והשני אלא בתנאי הכתוב ב- else - והתוכנית מחזירה את הערך (fib(n-1)+fib(n-2 כלומר (fib(2)+fib(1 ולכן מריצה שוב את הפונקציה fib (כמו בלולאות האחרות שלמדנו עליהן) על המספרים האלה. (fib(2 ילכד בתנאי השני ויחזיר את הערך 1 ו- (fib(1 נלכד ב- if הראשון ומחזיר את הערך 0 ולכן התוכנית מחזירה את הסכום שלהם (0+1) השווה ל- 1. לכן האיבר השלישי בסדרת פיבונאצ'י שלנו הוא 1. אם נחפש את האיבר הרביעי בסדרה (n==4) הלולאה שלנו תרוץ כמה פעמים – פעם ראשונה תחזיר (fib(3)+fib(2 לגבי (fib(2 אנו יודעים שהערך הוא 1 לגבי (fib(3 אנו יודעים שהתוכנית רצה כפי שהראנו בתוכנית הקודמת כשחיפשנו את האיבר השלישי ובסוף היא מחזירה את הערך 1 – כך שסכום האיברים בסדרת פיבונאצ'י (השני והשלישי) הוא 1+1 וזה שווה ל- 2. כך שהאיבר הרביעי בסדרה הוא 2. וכך הלאה התוכנית תרוץ מספר סיבובים גדול יותר, ככל שמיקום האיבר בסדרת פיבונאצ'י הוא גדול יותר (כל פעם תחשב את סכום שני האיברים הקודמים) ולבסוף תחזיר את ערך סכום שני האבירים שלפני ה n שאותו ביקשנו לבדוק. לבסוף – כדי לקבל סדרה של עשרת מספרי הפיבונאצ'י מהראשון (שהוא 1 ולא 0) ועד לעשירי (כתבנו 11 כי האחרון אינו כלול) אנו מריצים לולאת for בשילוב עם פקודת range שתדפיס את ערך הפיבונאצ'י מהאיבר הראשון עד העשירי (כולל העשירי). נעשה עוד אחד – הפעם כזה שמחשב את הפעולה המתמטית עצרת ! – חחח זה לא מסוג הדברים שניתן להבין בקריאה סתמית בטח לא בקריאה של פעם אחת, תנסו לעקוב אחר השלבים לאט לאט. חישוב עצרת ! בפונקציה רקורסיבית עצרת (factorial בלעז) של מספר כלשהו היא מכפלת כל המספרים הקודמים לו (לא כולל 0 – אחרת זה תמיד 0) במספר עליו מבצעים את הפעולה. למשל !5==1*2*3*4*5 (ישים לב הקורא שאני משתמש ב- == כמו בפייתון ולא ב - = אחד כמו המורה שרה ביסודי). def fact(n): if n==1: return 1 else: return n*fact(n-1) print(fact(4)) >>> 24 שוב הגדרנו פונקציה בשם fact היודעת לחשב עצרת עבור כל מספר n. כאשר בתוך הגדרת הפונקציה, עושים שימוש בפונקציה עצמה. כדי לחשב את תוצאת העצרת של המספר 4, נעקוב אחר הלולאה שמבצעת התוכנית – התוכנית בהתחלה לא נלכדת בתנאי הראשון של if n==1 ולכן מחזירה (4*fact(3. את (fact (3 היא מחזירה ללולאה והיא מקבלת (3*fact(2. את fact(2) היא מחזירה ללולאה ומקבלת (2*fact(1) . fact(1) מחזיר את הערך 1 ועכשיו עושים את כל הדרך חזרה – ומקבלים ש (fact(2 שווה 2. ו-(fact(3 שווה (3*fact(2 ולכן שווה ל- 6 ולכן (fact(4 שווה ל- 4*6 שהם 24. ננסה לסדר את אופן החישוב שמבצעת התוכנית בטבלה:
- תרגיל – בניית משולש פסקל באמצעות פונקציה רקורסיבית
תרגיל – בניית משולש פסקל באמצעות פונקציה רקורסיבית משולש פסקל הוא מעין פירמידה שמתחילה במספר 1 וככל שיורדים בפירמידה, כל מספר שווה לסכום שני המספרים שמעליו – נציג את ארבע הקומות העליונות של הפירמידה (אפשר להמשיך בלי סוף במורד הפירמידה): 1 1 1 1 2 1 1 3 3 1 רמז – פסקל מזכיר במבנה שלו את סיפור פיבונאצ'י רק במקום לחשב שני מספרים קודמים בסדרה אנו מחפשים סכום של שני מספרים מסוימים בשורה למעלה. רמז 2 – אפשר לכתוב תוכנית שתייצר רשימה עבור כל שורה במשולש פסקל, כך למשל שורה מס' 1 תכיל איבר אחד [1] השורה השנייה שני איברים [1,1] השורה השלישית , שלושה [1,2,1] וכו'. אחד הפתרונות - def pas(n): if n == 1: return [1] else: row = [1] uprow = pas(n-1) for i in range(len(uprow)-1): row.append(uprow[i] + uprow[i+1]) row += [1] return row for i in range (1,8): print (pas(i)) >>> [1] [1, 1] [1, 2, 1] [1, 3, 3, 1] [1, 4, 6, 4, 1] [1, 5, 10, 10, 5, 1] [1, 6, 15, 20, 15, 6, 1] הסבר – בנינו פונקציה רקורסיבית בשם pas המייצרת את איברי השורה הרלבנטית (n) במשולש פסקל. את השורה הראשונה אנו מייצרים באופן "ידני" באמצעות יצירת רשימה בעלת איבר אחד [1] כאשר n שווה 1. אחרת, כאשר n גדול מ- 1 אנו מייצרים רשימה חדשה בשם row (ואנו כבר יודעים שהאיבר הראשון בה יהיה 1, כי כך מתחילות כל השורות במשולש פסקל). כמו כן, אנו מייצרים משתנה נוסף בשם uprow שגם הוא יהיה רשימה בסופו של דבר, אליו נכניס שוב פעם את הפונקציה pas הפעם מופעלת על השורה הקודמת הגבוהה יותר בפירמידה (n-1) . כעת מוסיפים לשורה row את סכומי המספרים שמעל וסוגרים את השורה באמצעות הוספת המספר 1, הסוגר כל שורה במשולש פסקל. כאשר uprow מפסיק להיות נוסחה והופך לרשימה [1] יש לנו ב- row את הערך [1] , בשלב זה for i in range(len(uprow)-1) לא יעשה שום דבר כי האורך של uprow הוא 1 אבל האורך המתקבל מ- len(uprow)-1 הוא ==1-1, כלומר 0 ולכן לולאת ה for הזאת לא תרוץ כלל (for i in range(0 עושה משהו 0 פעמים, אבל אנחנו נעבור לשורה הבאה בקוד [row += [1 אשר מוסיפה לרשימה row את המספר 1 והוא הופך ל- [1,1] . נדגים בטבלה את התהליך הרקורסיבי של יצירת השורה החמישית n==5 במשולש פסקל – כפי שניתן לראות, פעולת הרקורסיה אינה תמיד קלה להבנה, ויתכן שידרש לאנשים שלא נתקלו בתופעה קודם לכן, יותר זמן. לפעמים היא מיועדת, שלא בצדק, לשמור על מקום העבודה של מי שכותב את הקוד, תחת ההנחה שכשאף אחד חוץ ממך לא מבין מה כתוב, תמיד יצטרכו אותך. אני לא מאמין בזה. אני מאמין בקוד קריא גם אם הוא מעט ארוך יותר, אבל לצורך פיתוח האינטלקט אין טוב מזה.
- פקודת input – דו שיח עם המשתמש
פקודת input – דו שיח עם המשתמש התוכנית שלנו יכולה לנהל שיחה עם המשתמש ולהגיב בהתאם באמצעות פקודת input, בואו נראה איך היא עובדת – n=input ("what is your name?") print ("Hello",n) >>> What is your name? כעת התוכנה ממתינה שהמשתמש יקליד את שמו – המשתמש מקליד את שמו נניח, סתם זורק, Eddie ולוחץ על Enter כדי שהיא תדע שהוא סיים. התוכנית כותבת Hello Eddie כדי לדרוש נתון מהמשתמש אנו מכינים משתנה שיקלוט את הנתון, בתוכנית למעלה המשתנה הוא n. כעת אנו כותבים את הפקודה input ולאחריה בסוגריים הנחיה למשתמש או שאלה. בהמשך אנו מחליטים מה לעשות עם הנתון הזה (שכעת נמצא בתוך n) – במקרה שלנו הדפסנו את המילה Hello לפני שמו של המשתמש כפי שהועבר לנו. ברירת המחדל של פייתון כאשר משתמשים בפקודת input היא להניח שהנתון שנכתב הוא מסוג string – מחרוזת. על מנת להראות לתוכנה שאנו מבקשים מספר (מסוג integer) נצטרך להגדיר את ה- input מעט אחרת – n=int(input("enter number")) ה- int הוא כדי להגדיר את הערך שיוזן על ידי המשתמש כמספר שלם integer. משחק הניחושים כעת נבנה משחק ניחושים – שבו המחשב יבחר מספר סודי (בהמשך זה יכול להיות באמת סודי אם נשתמש בפקודה random שנמצאת בספריה מיוחדת) ולנו יהיו שלושה ניחושים למצוא את המספר הזה, המחשב בכל פעם יגיד לנו אם המספר שלו הוא גדול או קטן מזה שאנחנו מנחשים עד שאנו נפסלים או מנצחים (מה שמסומן מימין לסולמית הוא תשבות המשתמש- אם כבר הזכרנו את סולמית, בפייתון אם כותבים סולמית #, התוכנה מתעלמת מכל מה שכתוב מימין לה, כך שזה משמש לכתיבת הערות בגוף התוכנית בלי שזה פוגע בביצועים). secretNum=57 for i in range (3): n=int (input ("what is your guess?")) if n==secretNum: print ("you win") break if n>secretNum: print ("your number is too big") if n<secretNum: print ("your number is too small") if i==2: print ("you lose") >>> what is your guess? #56 your number is too small what is your guess? #57 you win ואם מחטיאים שלוש פעמים: what is your guess? #20 your number is too small what is your guess? #39 your number is too small what is your guess? #60 your number is too big you lose הסבר – המספרים בצד השמאלי של הטבלה הם התשובה לשאלות ששאל המחשב. בשלב זה של חיינו, לפני שלמדנו את פקודת random (בפרק העוסק בספריות מוכנות) אנו קובעים בעצמנו מהו המספר הסודי או שמישהו קובע אותו ומישהו אחר מנסה לנחש וכך זה פחות מוזר, אוליי. קבענו משתנה בשם secretNumber והחלטנו שהוא יהיה, למשל, 57 . יצרנו לולאה באמצעות שימוש בפקודת for ובפקודת range(3) כך שהלולאה תרוץ שלוש פעמים (כל פעם i יהיה שווה למספר אחר, החל מ- 0 ועד 3 (לא כולל 3 אבל כן כולל את 2 ויחד עם 0 זה שלוש פעמים). התנאי הראשון בלולאה הוא מקרה של ניחוש נכון של המספר הסודי – ובמקרה כזה אנו רוצים להדפיס you win ולסיים את המשחק. ולכן אם מתקיים התנאי הזה, יש לנו פקודת break ששוברת את הלולאה ומסיימת את עבודתה באופן מיידי. שני התנאים הבאים בודקים האם המספר שניחשנו n הוא גדול או קטן מהמספר הסודי ומגיבים בהתאם. התנאי הרביעי if i==2 בודק האם הלולאה הגיעה לסיבוב השלישי והאחרון (בלי שהיה ניצחון אחרת היא היתה נשברת קודם לכן) ואם כן, מודפס המשפט המשפיל – you lose, שלאחריו הלולאה מסיימת את עבודתה שהוגדרה מראש כשלושה סיבובים בלבד.
- מחלקה class
מחלקה class בתוכניות מורכבות, שתכליתן לפתור בעיות כאלה ואחרות, אנו בעצם בונים מודל של החיים עצמם. פעמים רבות אנו הופכים את החיים למודל מתמטי כזה או אחר ומנסים לפתור איזושהי בעיה. למשל, כאשר אנו רוצים לפתור את החידה המפורסמת של שמונה המלכות, המבקשת מהפותר להניח 8 מלכות על לוח השחמט במיקומים כאלה שאף מלכה (שיכולה לזוז לכל כיוון ולכל מרחק) לא תעמוד על נתיב של מלכה אחרת, לחידה יש פתרון ויש דרכים יפות לפתור אותה באמצעות פייתון. במקרה כזה אנו צריכים לבנות באמצעות התוכנה, מודל של החיים האמיתיים, הכולל לוח שחמט, מלכה, אופן התזוזה של המלכה, המקומות שהמלכה חולשת עליהם בכל נקודה שהיא עומדת על הלוח, מתי מתקיימת התנגשות בין שני נתיבים של שתי מלכות או יותר וכו'. אפשר לפתור את החידה באמצעות מודל שנבנה ממערכת של פונקציות, אבל אפשר לעשות זאת באמצעות גישה אחרת של בניית אובייקטים בעלי תכונות ויכולות המדמים בצורה אינטואיטיבית יותר את החיים האמיתיים ופעמים רבות מאפשרים בנייה של מודל מדוייק יותר, בהיר יותר ויעל הרבה יותר. לכן, החלק הזה, המתייחס למחלקה, אינו לגמרי המשך ישיר של החלקים הקודמים, הוא במובן מסוים כמו יקום מקביל, המקנה סט נוסף של כלים לבניית מודלים באופן אינטואיטיבי יותר. הקונספט שהוסבר והודגם עד כה מתייחס לתכנות שאנו קוראים לו תכנות פונקציונאלי (בטח אפשר לקרוא לו אחרת), אנו משתמשים בפונקציות כדי לבצע מערך של פעולות בסדר לוגי מסוים. באמצעות יצירת מחלקה (class), פייתון מאפשרת בניית מודל הכולל מבנים גדולים ומורכבים או למעשה יצירת מפעל שיכול לסייע למתכנת ליצור במהירות פרט או בשמו המקצועי מופע (instance) שהוא כמו ייצור בעל תכונות ויכולות שהגדרנו במחלקה, והוא יהיה דומה ליתר המופעים שאותה מחלקה מסוגלת לייצר, למעט בחלקים שנגדיר כמיוחדים לכל אחד מהפרטים (או המופעים) במחלקה. המחלקה מאפשרת לנו ליצור מופע במהירות, והוא יקבל מיד את כל התכונות (attributes) והפונקציות (methods) שהוגדרו במחלקה (פונקציה בתוך מחלקה נקראת גם מתודה)– תכונה יכולה להיות צבע למשל ומתודה יכולה לעשות פעולה כמו הדפסה הכפלה וכיוב'. בדוגמא של חידת שמונה המלכות, אנו יכולים לבנות מודל של מלכה על לוח שחמט, ולאחר שבנינו את המודל, יש לנו מפעל קטן, אשר בשורת קוד אחת, הופך כל משתנה למלכה עם כל התכונות וכל הפונקציות שמלכה יכולה לבצע. כך אנו יכולים בין רגע להפוך את Q1 למלכה הראשונה, Q2 למלכה השנייה וכו' מכאן גם הגדרת פייתון כתכנות מונחה עצמים object oriented programing או בקיצור OOP, לעומת שפות תכנות שהן בעיקר פונקציונאליות ועושות שימוש בפונקציות. נכתוב תוכנית המייצרת מחלקה פשוטה ונסביר. class BlueMen (): color="blue" def m (self): print("moshe") def y (self): print ("yossi") x=BlueMen() x.m() >>> moshe x.y() >>> yossi print(x.color) >>> blue ונסביר - אנו מגדירים מחלקה באמצעות שימוש במילה class ולאחריה אנו בוחרים שם למחלקה BlueMen. בפייתון קיימת מוסכמה ששמות מחלקות מתחילים באות גדולה. אנו בחרנו בשם BlueMen ובתוך המחלקה שלנו לכל הפרטים שניצור תהיה תכונה (attribute) משותפת – הם יהיו כחולים blue)). כמו כן, בנינו שתי מתודות (שהן פונקציות בתוך המחלקה), האחת נקראת m והיא מדפיסה את המילה moshe והשנייה נקראת y והיא מדפיסה את המילה yossi. המילה self (גם היא מוסכמה ואפשר להשתמש במילה אחרת) בכל אחת מהמתודות שבנינו (מתודה m ומתודה y) מייצגת את הפרט הספציפי או המופע (instance) שאנו נייצר בעתיד באמצעות המחלקה שלנו. היות שבשלב בניית המפעל אנו לא יודעים איזה שם ספציפי יקרא לכל אחד מהפרטים שניצור באמצעותו, אנו קוראים לו self. בהמשך, לאחר שניצור פרט ונקרא לו x נוכל להשתמש ב- x כדי למצוא תכונות או להפעיל פונקציות בהתאם למה שהגדרנו במחלקה. כדאי לשים לב שאין פקודת print תחת הגדרת color כך שאם רוצים לראות על המסך את התכונה הזאת, אנו צריכים לבקש זאת באמצעות הפקודה print בניגוד למתודות m ו- y שכבר כוללות את הפקודה print. לאחר שבנינו את המחלקה – אנו יכולים בהקלדה של שורה אחת ליצור מופע בשם x שיקבל את כל תכונות המחלקה BlueMen ועושים זאת באמצעות סימן = מהצד השני של המשתנה שבחרנו למופע. לאחר שיצרנו את המופע x, השימוש בתכונות ובפונקציות הוא פשוט. מוסיפים אחרי ה-x נקודה ואת שם המתודה ובום – היא עושה מה שהיא אמורה לעשות, קרי ()x.m מדפיסה את המילה moshe ו- ()x.y מדפיסה את המילה yossi וכל מי שירצה לדעת מה צבעו של האובייקט יוכל להדפיס את x.color ולראות שהאובייקט שלנו הוא בעל תכונה משונה – הוא כחול. עוד דבר שכדאי לשים אליו לב הוא שפונקציות מסויימות יוצאות לפועל רק כאשר יש סוגריים (ריקים) אחריהם, וזה האופן שבו קוראים לפונקציה, יש לכך הסבר מתקדם יותר המשתייך לשלב אחר. לאחר שהקמנו את המחלקה אנו יכולים לייצר מופעים רבים בהשמה פשוטה של שם המחלקה ()BlueMen שבחרנו, לתוך משתנה נניח y. מרגע זה כל מה שיכולנו לבצע עם x נוכל לבצע גם עם y. חשוב לשים לב כי לגבי חלקים מסוימים מהיכולות של פייתון שהכרנו בתכנות הפונקציונאלי שעל מנת להשתמש בהן, לעיתים קרובות נצטרך מילות מפתח מיוחדות, לעיתים כאלה המתלווה אליהן קו תחתון או שניים לפני ואחרי המילה. היישום אינו באותו האופן כמו בחלק הפונקציונאלי, צריך ללמוד ספציפית את הביטויים המקבילים המיועדים למחלקה ואיך עושים דברים כאשר רוצים להשתמש בחלק הזה של פייתון. למעלה ראינו מחלקה של אנשים כחולים שאינה מקבלת פרמטרים כלשהם מהמשתמש. כעת נראה מחלקות המיועדות לייצירת פריטים, שהמשתמש צריך להזין, עם הקמת הפריטים (istances), נתונים כאלה ואחרים המשתנים בין פרט לפרט (בשפה מקצועית- בין מופע למופע, ובאנגלית from one instance to another). פונקציית __init__() במחלקה פעמים רבות נרצה שאובייקט יהיה בעל תכונות ייחודיות, אם האובייקט שלנו הוא למשל לקוח (בתוכנה שעושה דברים עם לקוחות), אזי יהיה לו למשל שם וגיל האופייניים לו, ולא רק משהו אוטומטי שהמחלקה מייחסת לכל הלקוחות. class Customer: def __init__(self, name, age): self.name = name self.age = age self.character="always right" def welcome(self): print("welcome",self.name,"!") j=Customer ("jack",47) j.welcome() >>> welcome jack ! print (j.name) >>> jack print (j.age) >>> 47 print (j.character) >>> always right פונקציית ()__init__ שייכת לקבוצת הפונקציות המיוחדות (special methods) בפייתון והיא נרשמת עם שני קווים תחתונים לפני ואחרי המילה, במקרה הזה המילה היא init. פונקציות אלה נקראות לעיתים dunders שזה קיצור של double underscore על שם הקו התחתון הכפול המאפיין אותן או slots. פונקציות אלה מיועדות לקריאה על ידי המערכת (מכאן גם חוסר האסתטיות היחסית של אופן הרישום) והיא מיועדת להגדיר את האובייקט (שניצור בעתיד באמצעות המפעל הקטן שלנו). למה לא הסתפקו בקו תחתון אחד ? התשובה היא שיש ביטויים שנכתבים עם קו תחתון אחד לפני המילה (בלי אחד אחרי) ויש כאלה שיש גם קו אחד לפני וקו אחד אחרי והם מיועדים לתפקידים אחרים. לאחר שבנינו מחלקה המגדירה לקוח (Customer), המפעל שלנו מוכן. כעת אנו מכריזים על המשתנה j כלקוח ששמו jack וגילו הוא 27 באמצעות השורה (j=Customer ("jack",47 , מאותו רגע, j נקרא מופע (instance) של המחלקה Customer, והוא יקבל מיידית שלל תכונות ויכולות המוקנות לחברים במחלקה Customer. כך למשל מאותו רגע jack, כמו אשתי, תמיד צודק, תכונה זו תהיה משותפת לכל הלקוחות (המופעים), משום שזו תכונה שאינה ספציפית רק ללקוח מסוים. אנו רואים ש- self מהמפעל הופך לאובייקט בשם j אשר חלק מהתכונות שלו הן תכונות משתנות כמו שם וגיל, וכל לקוח יקבל שם וגיל שונים, לעומת character שאינו מצוי ברשימת המשתנים משום שהוא קבוע – כל הלקוחות תמיד צודקים (always right). בנוסף, במחלקה שלנו קיימת מתודה שמבצעת פעולה מסוימת, היא מדפיסה ברכה אישית (עם שם המשתמש הספציפי) ברגע שמפעילים אותה ()j.welcome – מקבלים ! welcome jack . נדגים מדוע הפעולות שעובדות בתכנות הפונקציונאלי לא עובדות באותו האופן במחלקה (class) – נוסיף למחלקה שלנו משתנה נוסף customersNumber המשותף לכל המופעים היות שהוא ברמת המחלקה, שמאפשר לספור את מספר המופעים שיצרנו במחלקה. וניצור שני מופעים (שני לקוחות שונים), j ו- m. כשנבדוק כמה מופעים יש לנו נקבל את המספר 2. אולם גם אחרי שנמחק מופע אחד (נפטר את העובד), זה לא מסתדר אוטומטית, עדיין אנו רואים ספירה של שני מופעים, למרות שמחקנו אחד. class Customer: customersNumber=0 def __init__(self, name, age): self.name = name self.age = age self.character="always right" Customer.customersNumber +=1 def welcome(self): print("welcome",self.name,"!") j=Customer("jack",47) m=Customer("mark",26) print(Customer.customersNumber) >>> 2 del(m) print(Customer.customersNumber) >>> 2 כדי שדברים יגיבו בתוך המחלקות כמו שהם מגיבים בתכנות הפונקציונאלי הרגיל, צריך לכתוב קוד מיוחד, פונקציות מיוחדות, שיאפשרו מה שברור ומובן מאליו בתכנות הפונקציונאלי ומובנה בתוך פייתון. כך למשל, כדי לתקן את הבאג ולאפשר לנו לפטר עובד מהמערכת, אחרי שאנו מבצעים פקודת del, בקוד תכנות המחלקה נוסיף מתודה מיוחדת (__del__) שתאפשר גם פעולות מחיקה – class Customer: customersNumber=0 def __init__(self, name, age): self.name = name self.age = age self.character="always right" Customer.customersNumber +=1 def welcome(self): print("welcome",self.name,"!") def __del__(self): Customer.customersNumber -= 1 j=Customer("jack",47) m=Customer("mark",26) print(Customer.customersNumber) >>> 2 del(m) print(Customer.customersNumber) >>> 1 איך היינו עושים זאת בתכנות פונקציונאלי? מוחקים באמצעות פקודת del אפשר במילון ואפשר ברשימה כמו שרואים למטה - dic = {'a':'b', 'c':'d'} del dic['a'] print(dic) >>> {'c': 'd'} lista=["a","b","c"] del(lista[1]) print(lista) >>> ['a', 'c'] כדי ש del- (כמו פקודות טריוויאליות אחרות) תעבוד כמצופה ממנה בתוך מחלקות, אנו צריכים לבנות את היכולת הזאת באמצעות פונקציות מיוחדות כמו (__del__). אפילו עניין פשוט כמו להדפיס שם למופע (instance) שלנו יכול להיות מורכב (אבל שווה את הטרחה) ומחייב פונקציה מיוחדת – נסתכל על הפונקציה המיוחדת (__str__) בלעדיה לא נקבל משהו מובן כאשר נבקש להדפיס את האובייקט שלנו – class Coordinate: def __init__(self, letter, num): self.letter = letter self.num = num c =Coordinate('c', 35)) print(c) >>> <__main__.Coordinate object at 0x00000234DEF81828> כפי שניתן לראות, כאנו מבקשים להדפיס את המופע שלנו, אנו מקבלים את הצורה שבה פייתון רואה את האובייקט שיצרנו ואולי את המקום שלה בזכרון במחשב, שום דבר שאנו יכולים לעבוד איתו. אותנו מעניין, ייצוג של האובייקט באמצעות מחרוזת שתבהיר לנו במה מדובר (כמה שהדברים היו פשוטים בתכנות הפונקציונאלי). לכן נוסיף את שורת הקוד הבאה (מודגשת) - class Coordinate: def __init__(self, letter, num): self.letter = letter self.num = num def __str__ (self): return self.letter+str(self.num) y =Coordinate('a', 8) z=Coordinate("b",52) print(y) >>> a8 print(z) >>> b52 מה שעשינו הוא לבקש מהתוכנה, שבכל פעם שאנו מבקשים להדפיס את האובייקט שלנו, אנו רוצים לראות מחרוזת המורכבת מהאות (letter) שהכנסנו עם הקמת המופע, בצירוף המספר (num) שאף הוא הוזן עם הקמת המופע. כמובן שכדי לחבר מחרוזת (string) עם מספר (integer) אנו צריכים להפוך את המספר למחרוזת באמצעות הפקודה ()str, כך ש- (str(self.num הוא בעצם מחרוזת ולא integer, והוא כמו כל אות אחרת ולכן אפשר להדביק אותו באמצעות סימן + פשוט למחרוזת אחרת. כך אנו מקבלים אובייקט z למשל שאנו מבינים שמכיל את הקואורדינטה b52. יכולנו כמובן לבחור בדרכי ייצוג אחרות לתוכן האובייקט z, כל דבר שיבהיר לנו במה מדובר. ישנה פונקציה מיוחדת שעושה דבר דומה מאוד והיא נקראת (__repr__). ההבדלים בינה לבין פקודת (__str__) אינה מעניינו של שלב זה (אבל כדאי לדעת שזה קיים). כמה פונקציות מיוחדות יש? הרבה מאוד. ניתן לקרוא אודותיהן באתר של פייתון או במקומות אחרים ברשת (=מרשתת=אינטרנט).
- תרגיל – איקס מיקס דריקס
תרגיל – איקס מיקס דריקס בתרגיל הזה ננסה לבנות באמצעות מחלקה (וכמה שטיקים שלמדנו בסעיפים מוקדמים יותר), מודל פשוט של לוח המשחק איקס מיקס דריקס (איקס עיגול או באנגלית tic tac toe). על הלוח להיות מיוצג באופן שנוכל לראות אם בלוח יש תא ריק, תא שיש בו X או תא שיש בו O . כמו כן, נוכל להכניס X או O בתא שנבחר וכן למחוק במידה והתחרטנו. אפשר לנסות לבד (מומלץ) ואפשר להבין את הפתרון שלנו. נתחיל בכך שנקים את מפעל הלוחות שלנו – class BoardTicTacToe: def __init__(self): self.board=[f"{i}" for i in range (1,10)] קראנו למחלקה שלנו BoardTicTacToe, היות שבהקמת המפעל, אין צורך להכניס פנימה משתנים, אנו לא צריכים את הסוגריים אחרי שמה של המחלקה, פייתון יודעת לעשות את העבודה גם בלי זה. הלוח במציאות הוא דו מימדי (3X3), אבל לצורך המודל, אנו יכולים לייצג את הלוח כרשימה. המצב ההתחלתי של הלוח הוא שכל תשעת התאים שלו ריקים ולכן בחרנו בשורת קוד של list comprehension המכניסה מחרוזת קטנה המכילה מספר (המדמה תא ריק - אחד מתשע בלוח שלנו), כאשר כל מספר מייצג מקום מסוים על גבי הלוח שלנו. באמצעות הפקודה for ו- range אנו מייצרים תשעה איברים ברשימה שכל אחד מהם מכיל מחרוזת עם רווח אחד. האיבר הראשון ברשימה (צד שמאל למעלה בלוח) מיקומו על פי פייתון – 0 אבל, היות שהמשחק שלנו כולל את האות האנגלית O וזה עלול לבלבל את השחקן שלנו, אנו לא נתחיל את המספור של התאים באפס אלא ב- 1. בהמשך נצטרך להודיע לפייתון שכאשר אנו רושמים את המיקום 1, אנו בעצם מתכוונים למיקום 0 ברשימה. האיבר האחרון ברשימה שלנו המייצג את התא הימני ביותר למטה מיקומו 8 אבל מיוצג באמצעות הספרה 9. עכשיו כדי לראות מה יצא לנו, אנו צריכים ייצוג נורמלי וקריא של הלוח, ולכן נוסיף מתודה מיוחדת __str__ למחלקה, וגם נרצה שהתוכנית תייצג את הרשימה בצורה של שלוש שורות ולא בשורה אחת – כאן גם נלמד ש n\ (בקסלש backslash ולא ה- slash הרגיל) בתוך מחרוזת מייצר לנו שורה חדשה. לאחר מכן נייצר מופע של הלוח שלנו בשם ticTac - נא לשים לב שכאן כבר צריך את הסוגריים אחרי שם המחלקה כדי לקרוא לה לייצר את המופע- ticTac=BoardTicTacToe() - ונדפיס לוח ריק (בלי X או O ) ונראה איך הוא נראה. class BoardTicTacToe: def __init__(self): self.board=[f"{i}" for i in range (1,10)] def __str__(self): return f"{self.board[0:3]}\n{self.board[3:6]}\n{self.board[6:9]}\n" ticTac=BoardTicTacToe() print(ticTac) וכך נראה הלוח שלנו אחרי הדפסה – ['1', '2', '3'] ['4', '5', '6'] ['7', '8', '9'] מה עשינו עד כה ? לקחנו לוח בצורך רשימה הלוח שלנו מוגדר במחלקה כ- self.board וביקשנו להדפיס כל פעם שלושה איברים אחרים מתוך הרשימה ולרדת לשורה חדשה (עשינו זאת גם אחרי הדפסת השורה האחרונה משום שאם מדפיסים כמה לוחות אנו רוצים שיהיה מרווח בין לוח ללוח). באמצעות format שלמדנו קודם לכן, כל תא מכיל מספר מתוך פקודת ה-range שלנו החל מ- 1 ועד 9. עכשיו הבה נוסיף מתודות ללוח שלנו על מנת שנוכל לכתוב X או O או למחוק אותם. class BoardTicTacToe: def __init__(self): self.board=[f"{i}" for i in range (1,10)] def __str__(self): return f"{self.board[0:3]}\n{self.board[3:6]}\n{self.board[6:9]}\n" def writeX(self,location): self.board[location-1]="X" def writeO(self,location): self.board[location-1]="O" def del_location(self,location): self.board[location-1] = str(location) ticTac=BoardTicTacToe() ticTac.writeX(1) ticTac.writeO(2) ticTac.writeX(3) ticTac.writeO(7) ticTac.writeX(5) ticTac.writeO(9) print(ticTac) למעלה אנו רואים הוספת 3 מתודות, writeX שמוסיפה ללוח X במיקום שאנו בוחרים בין 1-9 (באמצעות המשתנה location). אותו הדבר לגבי writeO המוסיפה את האות האנגלית O במיקום שאנו בוחרים. המתודה השלישית (del_location) תפקידה למחוק את הסימן (X או O) שרשמנו בטעות (אחרי שגילינו שככה נפסיד) ולהחזיר את המספר (המייצג בעצם תא ריק) שהיה שם קודם לכן. את עניין השבת המספר עשינו הפעם בלי format אלא עם הפקודה ()str אשר לעניין זה של ייצוג תא ריק בלוח עושה בדיוק את אותה עבודה – זה שם לנו מחרוזת של מספר. כזכור כל מיקום בין 1-9 צריך להיות מתורגם למיקום אמיתי ברשימה בין 0-8 ולכן אנו רואים את הקוד - [self.board[location-1, כלומר אם אנו רוצים לכתוב X בתא מספר 1 הרישום יתבצע בתא מס' 0 ברשימה (כפי שפייתון מבין את המיקומים ולא כפי שאנו החלטנו), כלומר בתא השמאלי העליון בלוח. לאחר שביצענו סדרה של פעולות בלוח ורשמנו x ו- o בכמה מיקומים – נראה איך הלוח שלנו נראה – התוצאה ['X', 'O', 'X'] ['4', 'X', '6'] ['O', '8', 'O'] כמעט כל הלוח מלא מלבד המקומות 4, 6, ו- 8 שעדיין ריקים. כעת נניח שהתחרטנו על הפעולה האחרונה שהיתה לרשום O במיקום 9 – נבצע מחיקה של מה שרשום במיקום 9 ונראה מה מתקבל – אם לאחר הכנסת O למיקום 9 נרשום - class BoardTicTacToe: . . . . . . ticTac.writeO(9) ticTac.del_location(9) print(ticTac) למעלה, מימשנו את המתודה del_location וקיבלנו לאחר הדפסת הלוח – התוצאה ['X', 'O', 'X'] ['4', 'X', '6'] ['O', '8', '9'] לאן ממשיכים מכאן – ראשית אנו צריכים שהתוכנית תציג את המשחק וכלליו וכן לוח ריק ותשאל אותנו שאלות, אולי האם תרצה להיות שחקן X או שחקן O, האם נרצה לשחק מול המחשב או מול חבר, התוכנה צריכה לנתח מהו מצב של ניצחון, כאשר יש רצף של שלושה סימנים זהים. התוכנה צריכה לשאול אם אנו רוצים להתחיל משחק נוסף או לצאת. כמו כן, כל התוכנית צריכה להיות בתוך לולאה, אולי while על מנת שנוכל להמשיך לשחק והמשחק יאתחל את עצמו, עד שנגיד לו אחרת. על מנת שלא לעצור כאן ולבאס, ועל מנת להדגים איך נראית אינטראקציה מול משתמש, נדגים עוד פונקציה שמקבלת input, ואשר מתכתבת עם המשתמש, במקרה הזה מי שמסמן במשחק X, שואלת אותו מה הוא בוחר ומדפיסה את הלוח לאחר הבחירה שלו - class BoardTicTacToe: . . . . . . ticTac=BoardTicTacToe() def takeInputFromX(): x=int(input("mr.X where would you like your X?\nnumber between 1 and 9")) ticTac.writeX(x) print(ticTac) takeInputFromX() אנו כותבים פונקציה בשם takeInputFromX() אשר, כשמה, מבקשת מהשחקן את המיקום שבו הוא רוצה לכתוב את x. בחרנו במשתנה פנימי בשםx לתוכו עתידה התוכנית להכניס את המספר שהשחקן יבחר (1-9). כדאי לשים לב שלפני שרשמנו input רשמנו את הפקודה ()int וזאת משום שב- input התוכנה לוקחת בחשבון שהמשתמש מקליד מחרוזת, ואם לא נגיד לה שאנו מתכוונים למספר, בהמשך לא נוכל לבצע פעולות כמו חיסור וחיבור המיועדים למספרים ונקבל באג. לאחר מכן אנו מבקשים שהמספר הנבחר יכנס לתוך המתודה writeX במופע המחלקה BoardTicTacToe שנקרא ticTac , אשר כאמור מקבלת פרמטר של המיקום בו אנו רוצים לסמן את ה – x, ולאחר מכן אנו מבקשים להדפיס את מופע הלוח ticTac (בהתאם לאופן שבו בחרנו לייצג אותו). לאחר כתיבת הפונקציה ()takeInputFromX אנו צריכים לקרוא לפונקציה כדי שתתחיל לעבוד – וזאת פשוט על ידי כתיבת שם הפונקציה (עם הסוגריים אחרי שם הפונקציה, הסוגריים צריכים להופיע גם בעת כתיבת קוד הפונקציה ולא לבלבל עם מחלקה שאפשר בלי סוגריים בעת כתיבת קוד המחלקה). נקבל את השאלה של המחשב בשתי שורות משום שכתבנו n \ אחרי סימן השאלה– התוצאה mr.X where would you like your X? number between 1 and 9 לוחצים עם המקש השמאלי של העכבר במקום שצריך לכתוב את התשובה שלנו. מקלידים למשל את הספרה 6 לוחצים על Enter ובום... ['1', '2', '3'] ['4', '5', 'X'] ['7', '8', '9'] יש לנו x במיקום מספר 6 והלוח כבר מודפס. ספוילר – לאחר שבונים משחק מול מחשב, חכם כזה שיודע היכן לשים את הסימן, אזי לא ניתן לנצח את המחשב אלא רק להגיע למצב של תיקו או להפסיד, משום שבמשחק הזה יש חשיבות למי מתחיל והיכן הוא מסמן את הסימון הראשון, ואם לומדים את הפטנט, אזי המשחק הזה אינו מתקדם לשום מקום, אלא רק לתיקו ואפשר לזרוק אותו לאשפה. כמובן אפשר לבנות מחשב יותר פראייר, שיתן לפעמים לנצח, נו טוב...
- ירושה
ירושה ניתן לבנות מחלקה השואבת באופן מלא את כל התכונות של מחלקה אחרת, כלומר, "לרשת" ממנה באמצעות שורת קוד פשוטה, את כל התוכן הרלבנטי לנו. class Store: def __init__(self, size, adr): self.size = size self.adr = adr self.balance=0 def __str__ (self): return self.adr def add (self,num): self.balance +=num def sub (self,num): self.balance -=num stim=Store(150,"brodetsky 15 Or Yeuda") print(stim.balance) >>> 0 stim.add(500) print(stim.balance) >>> 500 stim.sub(300) print(stim.balance) >>> 200 print(stim) >>> brodetsky 15 Or yeuda למעלה אנו רואים מחלקה (class) רגילה שנקראת Store היא יודעת לייצר מופעים (instance) כמו זה שיצרנו וקראנו לו stim היא מקבלת שני פרמטרים , גודל החנות וכתובתה. והיא יודעת להוסיף ולחסר סכומים ממאזן החנות שהוגדר על ידנו כ- self.balance. כעת נניח שאנחנו רוצים לבנות תת מחלקה של סופרמרקט Supermarket שהיא גם סוג של חנות Store על כל המאפיינים של החנות בשינוי אחד בלבד, והוא שבסופר מרקט מעניין אותנו גם כמה מקומות חניה צמודים אליו. כל יתר המאפיינים זהים. לכן במקום לכתוב את הקוד עבור מחלקת Supermarket מחדש, אנו יורשים ממחלקת Store את כל המאפיינים שאנו צריכים ומוסיפים פרמטר נוסף של מקומות חניה parknum. נראה איך זה עובד - class Store: def __init__(self, size, adr): self.size = size self.adr = adr self.balance=0 def __str__ (self): return self.adr def add (self,num): self.balance +=num def sub (self,num): self.balance -=num class Supermarket(Store): def __init__(self, size, adr,parknum): Store.__init__(self,size,adr) self.parknum=parknum ampm=Supermarket(400,"hashalom 45 ramalla",50) ampm.add (320) ampm.sub(150) print (ampm.parknum) >>> 50 print (ampm.balance) >>> 170 print(ampm) >>> hashalom 45 ramalla כתבנו את מילת המפתח class ובחרנו שם כמו בכל מחלקה רגילה Supermarket . אבל הפעם הכנסנו לתוך הסוגריים הצמודים לשם המחלקה, גם את שם המחלקה ממנה אנו רוצים לרשת את התכונות, במקרה שלנו Store. בנוסף הכנסנו תחת הפונקציה המיוחדת __init__ שלושה פרמטרים אשר מעניינים אותנו כאשר מדובר בסופרמרקט size, adr ,parknum גודל החנות, הכתובת שלה, ומספר מקומות החניה הצמודים לסופרמרקט (פרמטר שאינו מעניין כאשר מדובר בחנות באופן כללי). בנוסף, על מנת שנוכל להעביר ל- Supermarket את התכונות של Store אנו צריכים להוסיף את שורת ה- __init__ של המחלקה ממנה יורשים - (Store.__init__(self,size,adr וזהו. עכשיו ניצור מופע של סופרמרקט בשם ampm, שטחו 400 מ"ר, והוא נמצא ברחוב hashalom ויש לו 50 מקומות חניה צמודים. והנה הדבר המדהים, אנו יכולים להוסיף ולהחסיר סכומים ממאזן הסופרמרקט, דבר שאינו מופיע בכלל באופן גלוי בקוד המחלקה Supermarket , לבקש לראות את המאזן שלו (balance) ולראות את שם הסופרמרקט – הכל מבלי שהיינו צריכים לקודד שום דבר מהדברים האלה במחלקה החדשה, שירשה את כל התכונות הללו ישירות ממחלקת Store. זה הסיפור חסכון בשורות קוד. תכונת הירושה מזכירה פעולה דומה שניתן לבצע עם פונקציות, לקחת פונקציה בשלימותה, לעטוף אותה בפונקציה אחרת עם תוספת או שינוי פעולה זו נקראת decorating או קישוט פונקציה.
- פונקציה ממעלה גבוהה יותר - higher order function
פונקציה ממעלה גבוהה יותר - higher order function אנו לוקחים את האפשרות של החזרת פונקציה על ידי פונקציה אחרת על מנת להוסיף אפשרויות לפונקציה המקורית ולחסוך בשורות קוד. def shalom (name): return "shalom "+ name +"!" def shalomAndWelcome (name): return shalom(name)+" welcome!" print(shalom("eddie")) >>> shalom eddie! print (shalomAndWelcome("yossi")) >>> shalom yossi! Welcome! למעלה אנו רואים פונקציה שנקראת shalom המקבלת שם כפרמטר ומחזירה את השם בליווי ברכת שלום וסימן קריאה בסוף. לאחר מכן, כתבנו פונקציה נוספת שאף היא לוקחת שם של מישהו כפרמטר ומחזירה את הפונקציה shalom על אותו שם בתוספת המילה !welcome שלא הופיעה בפונקציה המקורית, כלומר אנו בסך הכל הוספנו את המילה !welcome כקישוט לפונקציה shalom בלי להעתיק את כל שורות הקוד של הפונקציה shalom שיכולות להיות רבות. אנו לוקחים את האפשרות של הכנסת פונקציה כמשתנה (argument) בתוך פונקציה אחרת על מנת להוסיף אפשרויות לפונקציה המקורית ולחסוך בשורות קוד. def plus (x,y): return x+y def minus (x,y): return x-y def multi (x,y): return x*y def div (x,y): return x/y def calcul (func,x,y): return "the answer is "+str(func(x,y)) print(calcul(multi,5,6)) >>> the answer is 30 print(calcul(plus,5,6)) >>> the answer is 11 למעלה, השקעתי בכם וכתבתי ארבע פונקציות העושות את ארבע פעולות החשבון הבסיסיות על שני פרמטרים מספריים (x,y). המתכנת שהגיע אחרי, רוצה להתחנף לבוס, וכותב פונקציה בשם calcul המקבלת שלושה פרמטרים, הראשון הוא פונקציה (יכולה להיות כל אחת מהארבע שכתבתי בזיעת אפי) השניים האחרים הם פרמטרים מספריים x ו- y , והיא מחזירה את תוצאת החישוב בליווי המשפט the answer is לשם הקישוט, הוא יכול לעשות זאת משום שפייתון מאפשרת את המבנה הזה. עניין טכני להתעכב עליו, היינו צריכים להפוך את התוצאות המספריות המתקבלות מהפונקציות השונות למחרוזת str (כזכור מספר יכול להיות integer או בפעולת החילוק float), משום שאחרת לא יכולנו להדביק את מספר (מסוגinteger או float) באמצעות + למחרוזת אחרת שהיא "the answer is". פונקציה המחזירה פונקציה או מקבלת פונקציה כמשתנה נקראת- פונקציה ממעלה גבוה יותר (higher order function). הוד מעלתה.
- פונקציית קישוט – decorator
פונקציית קישוט – decorator לו אני בתפקיד האקדמיה ללשון, הייתי בוחר את המילה קשטנית, לעת עתה נשאר עם פונקציית קישוט או דקורטור או פונקציה מקשטת, שהיא דרך מקוצרת ונוחה לקחת פונקציה קיימת, ולהוסיף עליה שכבת קישוט נוספת, מבלי להתעסק עם הקוד שלה – כמו בפונקציה ממעלה גבוהה יותר ותוך ניצול היכולות המתאפשרות בפונקציות ממעלה גבוהה יותר . הכי טוב לראות בדוגמא – def func (name): return "welcome "+name def plus (num): return num+42 def deco(f): def wrapper (*args): new=str(f(*args))+" you are the best" return new return wrapper print (func("eddie")) >>> welcome eddie print (plus(8)) >>> 50 @deco def func (name): return "welcome "+name @deco def plus (num): return num+42 print (func("eddie")) >>> welcome eddie you are the best print (plus(8)) >>> 50 you are the best כתבנו שתי פונקציות פשוטות אחת בשם func שמקבלת בצורת מחרוזת ומחזירה את השם בצירוף המילה welcome השנייה נקראת plus והיא מקבלת מספר integer ומחזירה מספר הגדול ממנו ב- 42. כעת נבנה פונקציית קישוט (decorator) בשם deco המקבלת פונקציה כמשתנה (f). המשתנה f מייצג את הפונקציה המקורית שלנו. לאחר מכן, נבנה תחתיה פונקציה חדשה בשם wrapper שתעטוף את הפונקציה המקורית ותבצע בה את השינוי המיוחל. תחת הפונקציה wrapper אנו קוראים לפונקציה המקורית f מבצעים את השינוי שאנו רוצים. אנו רוצים שהערך המתקבל יהפוך למחרוזת str (למקרה, למשל, שהערך הוא מספר שאינו מחרוזת) כדי שנוכל להדביק לו מחרוזת נוספת - "you are the best", שזה בעצם כל מה שתעשה פונקציית הקישוט שלנו לתוצר הפונקציה שהיא מקשטת. היות שהפונקציה המקורית שאנו מקשטים אמורה לקבל משתנים (ארגומנטים) אנו מוסיפים את המשתנה הכללי *args גם בפונקציית wrapper (שעוטפת את הפונקציה המקורית) וגם בקריאה לפונקציה המקורית (f(*args משום שאנו עדיין לא יודעים מה עושה הפונקציה שאנו עתידים לקשט ואילו ערכים היא מקבלת. כאן מגיע החלק המורכב – התוצר של פונקציית wrapper הוא תוצר חדש המשלב את תוצר הפונקציה המקורית. והתוצר של כל פונקציית הקישוט שלנו הוא פונקציית wrapper. כלומר אנו משלבים שני אלמנטים מרעיון הפונקציה ממעלה גבוהה יותר, גם קבלת פונקציה מקורית כמשתנה (f), וגם החזרת פונקציה חדשה (wrapper). היופי בהתגלמותו הוא בשימוש הפשוט בפונקציית הקישוט – כותבים @ (כרוכית=שטרודל) ואת שם הפונקציה המקשטת מימין לה, מעל הפונקציה שרוצים לקשט ובום...פונקציית הקישוט משנה את תוצר הפונקציה המקורית בלי שנגענו בקוד של הפונקציה המקורית. לאחר שקישטנו את הפונקציות שלנו, אנו מבקשים להדפיס את התוצרים עם אותם המשתנים כמו קודם, לתוצרים התווסף המשפט האלמותי – "you are the best" כולל המשפט העמוק - 50 אתה הטוב ביותר.. .
- map( ) filter( ) – פונקציות מובנות ממעלה גבוהה
( )map( ) filter – פונקציות מובנות ממעלה גבוהה בפייתון קיימות פונקציות מובנות, שיודעות לקבל פונקציה, ואיטרטור (כמו רשימה למשל) ולבצע את הפעולה שהפונקציה יודעת לעשות, על כל אחד מהאיברים באיטרטור. התוצר שלהן הוא איטרטור המייצר בכל פעם את התוצאה של הכנסת האיבר הבא באיטרטור כפרמטר בפונקציה. יאללה דוגמא – lista=[1,2,3,4,5] def func (num): return num**2 for i in map(func,lista): print (i) >>> 1 4 9 16 25 print([i for i in map(func,lista)]) >>> [1, 4, 9, 16, 25] def func2(num): if num < 3: return False else: return True print([i for i in filter(func2,lista)]) >>> [3, 4, 5] בתוכנית למעלה – נתבונן ראשית בפונקציה ()map שלוקחת פונקציה שקראנו לה func שמחזירה את ריבוע המספר שאנו מכניסים לפרמטר, ולוקחת רשימה שנקראת lista ומפעילה את הפונקציה על כל אחד מאיברי הרשימה. היות שהתוצר הוא איטרטור שבכל פעם מבצע את הפעולה על האיבר הבא ברשימה, כדי לראות את כל הרשימה עוברת תחת הפונקציה אנו זקוקים לפקודת for שתרוץ על האיטרטור שלנו ותשלוף את התוצר הבא בתור. אפשר לקבל את זה גם ברשימה באמצעות list comprehension ואפשר גם בדרכים אחרות. אפשרת גם לוותר על ()map לגמרי כפי שנראה בהמשך אבל קודם נראה מה פילטר, ()filter עושה. פילטר מסננת דרך הפונקציה רק תוצרים המוגדרים בפייתון כ – True ומוציאה החוצה תוצרים שהם False. לשם הדוגמא, נבנה פונקציה קטנה שמחזירה ערך של False למספרים הקטנים מ -3 ו- True למספר הגדולים או שווים ל- 3. כאשר נבצע פילטר של הפונקציה לרשימת המספרים שלנו lista נקבל רק את התוצרים המוגדרים לאחר הפעלת הפונקציה כ- True כלומר 3,4,5 וגם כאן היות שזה איטרטור אנו צריכים לבקש באופן מיוחד באמצעות פקודת for ו- list comprehension לקבל את כל תוצרי הסינון ברשימה מסודרת [3,4,5]. אפשר במקום ()map ופחות קוד להגיע לאותה תוצאה – lista=[1,2,3,4,5] print([i**2 for i in lista]) >>> [1, 4, 9, 16, 25] lista=[1,2,3,4,5] def func (num): if num%2==0: return num**2 else: return "kuku" print([func(i) for i in lista]) >>> ['kuku', 4, 'kuku', 16, 'kuku'] בדוגמא השנייה אנו רואים שאפשר גם כאשר הפונקציות מורכבות לבצע מיפוי בלי map() זה איטרטור אלא רשימה, ולכן יש הבדל בין השניים, אבל עושה פחות או יותר את אותה העבודה. כדאי לשים לב שאם לא היינו מבקשים בפונקציה השנייה להדפיס "kuku" כשהפרמטר הוא אי זוגי, היינו מקבלים None במקום, משום הפונקציה לא מחזירה כל ערך כאשר המספר אינו זוגי (num%2==0), כדי להימנע מכך היינו צריכים להוסיף תנאי if func(i)!=None ואז היינו מקבלים רשימה רק של ריבועי מספרים זוגיים [4,16] בלי מלל עודף.