פרק קודם:
פייתון 20 - למבדותמה נלמד
- מהו Decorator.
- כיצד כותבים אחד
- מה זה *args, **kwargs
עטיפה של פונקציות
יש לנו פונקציה 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.
Decorators
1 | def LogCall(func): |
Decorator - פונקציה שעוטפת או “מקשטת” פונקציה אחרת ומוסיפה התנהגות או משנה אותה.
תנסו להריץ את הקטע קוד הזה ותראו מה קורה.
הפונקציה שנקראת היא SayHi
ומוחזר הערך והוא מודפס.
אך יש משהו שונה בקריאה הזו הפעם - יש לה דקורציה נוספת של פונקציה שנקראת LogCall
שמדפיסה את שם הפונקציה המקורית.
הדקורציה לפונקציה היא בעזרת הסמל @
וזה נראה ככה:
1 |
|
כדי להבין את זה צריך לשים לב ל-2 דברים כאן:
- הגדרה של פונקציה העוטפת פונקציה אחרת
1 | def LogCall(func): |
אנחנו מגדירים פה שתי פונקציות נוספות:
LogCall - הפונקציה המקשטת שעוטפת פונקציות אחרות.
InternalLog - הפונקציה שמבצעת את ההתנהגות וקוראת לפונקציה המקורית.
אחרי שמוסיפים את הדקורציה LogCall
לפונקציה SayHi
אנחנו משנים את סדר הקריאות.
- החזרה של פונקציה
כמו שלמדנו בלמבדות - אנחנו יכולים להחזיר פונקציה מפונקציה אחרת כך שסדר הקריאות משתנה.
הפונקציה הפנימיתInternalLog
מוחזרת במקום הפונקציה המקוריתSayHi
.
הקריאה הזו:
1 | SayHi() |
בעצם קוראת לפונקציה InternalLog
שהיא בתורה קוראת לפונקציה המקורית SayHi
.
תרגיל
- תתקנו את קטע הקוד הבאה כך שיודפס “Correct” וה-Decorator יהיה נכון.
1 | global number |
- שימו לב ה-global הופך את המשתנה לגלובאלי ככה שאפשר לקרוא לו מכל התכנית.
זה לא נפוץ ולרוב לא כדאי להשתמש בו - רק לצורך התרגיל.
1 | global number |
כל מה שהיה חסר כאן זה קריאה נוספת ל-func
בתוך הפונקציה DoTwice
קבלה והחזרה של פרמטרים
1 | def LogCall(func): |
כדי לקבל פרמטרים ולהעביר אותם לפונקציה המקורית אנחנו משתמשים ב:
1 | *args, **kwargs |
וכדי להחזיר את הערך שמתקבל כל מה שעלינו לעשות זה לקרוא ל-return:
1 | return func(*args, **kwargs) |
מה זה *args
*args
משתמש באופרטור הכוכבית.
הכוכבית בפרמטר נותן לנו אופציה להעביר יותר מפרמטר אחד לפונק’ מבלי להגדיר רשימה כלשהי.
1 | def Sum(*arguments) -> int: |
פייתון בעצם מתייחס לערך הזה כ-Tuple. ניתן לצפות בזה או בדיבאג או עם הדפסה:
1 | print(f"Type of arguments: {type(arguments)}") |
זוכרים Tuple
? יוצרים אחד בעזרת הסוגרים המעוגלים! ().
מה זה **kwargs
פייתון מאפשר גם להעביר פרמטרים בעזרת שם:
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 לפרמטרים ללא שם.
שתי כוכביות יוצרות מילון לפרמטרים עם שם.
שימוש בכמה Decorators
ניתן להשתמש בכמה Decoratorים על פונקציה אחת, זה לא עסק מורכב וכל מה שאתם צריכים לעשות זה להוסיף אותם בסדר יורד:
1 | def LogBefore(func): |
במקרה הזה זה כמו לכתוב:
1 | SayHi = LogAfter(LogBefore(SayHi)) |
תנסו להריץ את זה - תראו מה קורה!
תרגילים
- כתבו פונקציית קישוט אשר סופרת כמה פעמים הפונקצייה נקראית.
- טיפ: תשתמשו במילון ובקבלת השם של הפונקציה ע”י
1
func.__name__
2.
כתבו פונקציית קישוט לפונקצייה MyFunc
אשר תדפיס את כמות הפרמטרים שהועברו אליה.
1 | def MyFunc(*args): |
- שמנו כובע Gray-Hat ואנחנו רוצים להרוס את פונקציית הקישוט הבאה כך שכל מחרוזת שהפונקציה המקורית מקבלת כל אות קטנה תהפוך לגדולה וכל אות גדולה תהפוך לקטנה!
להלן קטע קוד לעזר:
ניתן להניח שכל הפרמטרים הם מחרוזות.
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ים.
פייתון 22 - פונקציות קישוט חלק ב