פרק קודם:
פייתון 19 - למבדותיש לנו פונקציה SayHi
שמה שהיא עושה זה מדפיסה Hello World.
1 | def SayHi(): |
אנחנו רוצים להוסיף התנהגות מבלי לשנות את הפונקציה הזו.
ההתנהגות שאנחנו רוצים זה להדפיס את שם הפונקציה כשהיא נקראת.
בקריאה הזו:
1 | def SayHi(): |
אנחנו רוצים שתי הדפסות:
Calling Func SayHi
Hello World
השלב הראשון זה להדפיס את שם הפונקציה לפני הקריאה:
1 | def SayHi(): |
זו לא דרך טובה כי אנחנו חייבים להוסיף כל פעם מחדש את ה print
לפני כל פונקציה.
יש לנו את הלוגיקה של ההדפסה - אנחנו רוצים להשתמש בו שוב ושוב ולכן ניצור פונקציה ממנו.
הפרמטר של הפונקציה החדשה זו פונקציה!
1 | def LogCall(func): |
הוספנו קריאה נוספת והמטרה שלנו היא למזער את כמות השינויים.
מה שאפשר לעשות זה להכניס את הקריאה של SayHi
לתוך LogCall
.
1 | def LogCall(func): |
אנחנו מתקדמים למטרה, אך זה עדיין לא אידיאלי כי אנחנו רוצים להשתמש רק ב-SayHi
.
המטרה שלנו היא ש-SayHi
תדפיס גם את שם הקריאה כשקוראים ישירות ל-SayHi
.
כדי לשנות את סדר הקריאות אנחנו צריכים להשתמש בפונקציה חדשה, נקרא לה InternalLog
והיא תהיה פנימית בתוך LogCall
.
1 | def LogCall(func): |
מה שהפונקציה הפנימית מבצעת זה הדפסה וקריאה לפונקציה המקורית שלנו SayHi
.
הפונקציה LogCall
כעת מחזירה את הפונקציה הפנימית.
זוכרים אובייקטים? גם פונקציה היא סוג של אובייקט כאשר מתייחסים לשם שלה
שימו לב שלא ביצענו קריאה ל-InternalLog
.
קריאה לפונקציה כמו שלמדנו היא בעזרת סוגריים מעוגלים.
1 InternalLog()
כעת אנחנו רוצים לשנות את סדר הקריאות לפונקציה המקורית.
1 | def SayHi(): |
SayHi
. SayHi
תיקרא קודם ל-InternalLog
. InternalLog
תקרא ל-SayHi
המקורית.לשנות את סדר הקריאות כל פעם זה לא כל כך יפה ולכן פייתון מאפשרת לנו שימוש תחבירי יותר הולם.
וזה נקרא ה-Decorator.
1 | def LogCall(func): |
Decorator - פונקציה שעוטפת או “מקשטת” פונקציה אחרת ומוסיפה התנהגות או משנה אותה.
תנסו להריץ את הקטע קוד הזה ותראו מה קורה.
הפונקציה שנקראת היא SayHi
ומוחזר הערך והוא מודפס.
אך יש משהו שונה בקריאה הזו הפעם - יש לה דקורציה נוספת של פונקציה שנקראת LogCall
שמדפיסה את שם הפונקציה המקורית.
הדקורציה לפונקציה היא בעזרת הסמל @
וזה נראה ככה:
1 |
|
כדי להבין את זה צריך לשים לב ל-2 דברים כאן:
1 | def LogCall(func): |
אנחנו מגדירים פה שתי פונקציות נוספות:
LogCall - הפונקציה המקשטת שעוטפת פונקציות אחרות.
InternalLog - הפונקציה שמבצעת את ההתנהגות וקוראת לפונקציה המקורית.
אחרי שמוסיפים את הדקורציה LogCall
לפונקציה SayHi
אנחנו משנים את סדר הקריאות.
InternalLog
מוחזרת במקום הפונקציה המקורית SayHi
.הקריאה הזו:
1 | SayHi() |
בעצם קוראת לפונקציה InternalLog
שהיא בתורה קוראת לפונקציה המקורית SayHi
.
1 | global number |
1 | global number |
כל מה שהיה חסר כאן זה קריאה נוספת ל-func
בתוך הפונקציה DoTwice
1 | def LogCall(func): |
כדי לקבל פרמטרים ולהעביר אותם לפונקציה המקורית אנחנו משתמשים ב:
1 | *args, **kwargs |
וכדי להחזיר את הערך שמתקבל כל מה שעלינו לעשות זה לקרוא ל-return:
1 | return func(*args, **kwargs) |
*args
משתמש באופרטור הכוכבית.
הכוכבית בפרמטר נותן לנו אופציה להעביר יותר מפרמטר אחד לפונק’ מבלי להגדיר רשימה כלשהי.
1 | def Sum(*arguments) -> int: |
פייתון בעצם מתייחס לערך הזה כ-Tuple. ניתן לצפות בזה או בדיבאג או עם הדפסה:
1 | print(f"Type of arguments: {type(arguments)}") |
זוכרים Tuple
? יוצרים אחד בעזרת הסוגרים המעוגלים! ().
פייתון מאפשר גם להעביר פרמטרים בעזרת שם:
1 | def SayHiTo(name): |
מה ש**kwargs
נותן לנו זה ליצור מילון של שמות וערכים שמועברים כפרמטרים בפונקציה.
בעצם השימוש של זה הוא עם שני כוכביות לעומת כוכבית אחת בדוגמא הקודמת שהם פרמטרים ללא שם!
1 | def PrintData(**data): |
במקרה הזה נוצר לנו מילון!
מבינים למה יש לנו משתנים עם שמות ובלי שמות?
פרמטרים ללא שם נוצרים כ-Tuple כי אין להם שם.
אין להם מפתח שיכול להיוצג מיוצג במילון.
אך כאשר אנחנו קוראים לפונקציה ככה:
1 | PrintData(Name = "Bob", Age = 44) |
אנחנו בעצם יצרנו מילון עם הערכים:
1 | Name : Bob |
1 | def LogCall(func): |
כאשר אנחנו מעבירים את שניהם לפונקציה המקורית בתוך ה-Decorator אנחנו בעצם מעבירים את כל הפרמטרים עם שם ובלי שם.
אמ;לק
כוכבית אחת יוצרת Tuple לפרמטרים ללא שם.
שתי כוכביות יוצרות מילון לפרמטרים עם שם.
ניתן להשתמש בכמה Decoratorים על פונקציה אחת, זה לא עסק מורכב וכל מה שאתם צריכים לעשות זה להוסיף אותם בסדר יורד:
1 | def LogBefore(func): |
במקרה הזה זה כמו לכתוב:
1 | SayHi = LogAfter(LogBefore(SayHi)) |
תנסו להריץ את זה - תראו מה קורה!
1 | func.__name__ |
2.
כתבו פונקציית קישוט לפונקצייה MyFunc
אשר תדפיס את כמות הפרמטרים שהועברו אליה.
1 | def MyFunc(*args): |
1 | def ValidateArguments(func): |
1 | class FunctionCalls: |
אני רוצה להדגיש את החשיבה בעזרת OOP בתרגיל הזה.
במקום ליצור משתנים גלובליים עשיתי את זה בתוך מחלקות.
מחלקה היא עצם מאוד שימושי וכלי מעולה לפתרונות בעיות תכנות.
המשתנה Calls.Dictionary
מוחזק פעם אחת בתוך Calls
ולכן לא עשיתי גישה ישירה ל-FunctionCalls
.
1 | def LogNumOfArugments(func): |
תרגיל מאוד פשוט - כל מה שעלינו לעשות כדי לקבל את כמות הפרמטרים היא לקרוא לפונקצייה len
.
1 | def ValidateArguments(func): |
אני לא חובב גדול של תכנות נינג’ה אך הפעם הייתי צריך להדגים את היכולת הזו של פייתון.
יצירת ה-newArg2 הוא בעזרת הפיצ’ר של פייתון שנקרא - One Line Loop.
ניתן לפצל קוד ארוך לשורה בודדת, הקריאה של זה כמו עברית מימין לשמאל:
for char in name
.
* לאחר מכן התנאי if str... else str...
.
* ולבסוף ה-str.lower(char)
או str.upper(char)
בהתאם לתנאי.
* מה שה-join
עושה הוא הופך את הרשימה המוחזרת למחרוזת.
שימו לב שבקריאה יש סוגריים מרובעות שהן יוצרות לנו רשימה ולא מחרוזת:
1 | [str.lower(char) if str.isupper(char) else str.upper(char) for char in name] |
השני החלק עובר על שימושים נפוצים בפייתון בעזרת ה-Decoratorים.
פייתון 21 - פונקציות קישוט חלק ב