מודולים

הקדמה

מתכנתים העוסקים בקביעות במלאכתם, יזכו לעיתים תכופות להיתקל באתגרים בתחומים שונים.

דוגמה טובה לאתגר נפוץ שכזה היא יצירת מידע אקראי:

  • מתכנת שיוצר משחק קלפים יצטרך לכתוב קוד לשליפת קלף אקראי מחפיסת הקלפים.
  • מתכנת שמתקשה להחליט אם הוא רוצה להיות בפריז, ברומא או בקוטב הצפוני, יכתוב תוכנית שתבחר עבורו את היעד באקראיות.
  • מתכנת שרוצה לבנות סימולציות למצבים מהחיים האמיתיים, יצטרך פעמים רבות להשתמש במידע אקראי.

דוגמה טובה נוספת היא עבודה עם תאריכים:

  • מתכנת שרוצה לדעת בעוד כמה זמן מגיע תאריך מסוים (יום הולדת, לדוגמה).
  • מתכנת שרוצה לבדוק מה יהיה התאריך בעוד 100,000,000 שניות.
  • מתכנת שקיבל מסמך עם תאריכי הלידה של כל אוכלוסיית ישראל, ורוצה לדעת מהו החודש שבו נולדים הכי הרבה תינוקות בישראל.

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

הגדרה

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

שימוש

יבוא

ניקח כדוגמה את המודול random, שמטרתו לעזור לנו ליצור מידע אקראי.
לפני שנוכל להשתמש ביכולותיו של המודול, נצטרך לבקש מפייתון לטעון אותו בעזרת מילת המפתח import:


In [ ]:
import random

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

  1. בעזרת התיעוד באתר פייתון, שאליו אפשר להגיע אם כותבים python documentation random במנועי חיפוש.
  2. בעזרת הפונקציה dir – נוכל לכתוב dir(random).
  3. בג'ופיטר, אם נכתוב בתא קוד random. ונלחץ Tab ↹.

דוגמאות לשימוש

במשחק הפוקימון "פוקימון אדום" אפשר לבחור בתור הפוקימון ההתחלתי את בלבזאור, את סקווירטל או את צ'רמנדר.
על הפוקימון הטוב ביותר לבחירה ניטשים ויכוחים רבים עוד מאז שחרורו של המשחק ב־1996.
כדי לא להיקלע לעין הסערה, נבנה תוכנה שתבחר את הפוקימון עבורנו באופן אקראי.

התיעוד של המודול random, כולל פונקציה בשם choice, שבאמצעותה נוכל לבחור איבר אקראי מתוך iterable.
השימוש בתיעוד מומלץ במיוחד בהקשרי מודולים, שכן ההסברים שם בהירים, ופעמים רבות מובאות שם דוגמאות לשימוש בפונקציות של המודול.
נייבא את random ואז נשתמש ב־choice:


In [ ]:
import random

pokemons = ['Bulbasaur', 'Squirtle', 'Charmander']
starter_pokemon = random.choice(pokemons)
print(starter_pokemon)

לא היינו יכולים להשתמש ב־random.choice לולא היינו מייבאים את random.
מרגע שייבאנו את המודול – נוכל להשתמש בו לאורך כל הקוד.

השתמשו בפונקציה הנמצאת במודול random כדי לדמות הטלת קובייה בעלת 20 פאות (D20).
הגרילו מספר בין 1 ל־20, הפעם בעזרת פונקציה שהיא לא choice.
השתמשו במקורות המידע שצוינו כדי למצוא את הפונקציה המתאימה למשימה.

חשוב!
פתרו לפני שתמשיכו!

יתרונות

אלו, בין היתר, היתרונות של שימוש במודולים הקיימים בפייתון:

  1. פישוט: מודול לרוב יפתור לנו בעיה אחת, קטנה ומוגדרת היטב.
  2. הפרדה: המודול מופרד מהקוד שלנו, ישאיר את הקוד שלנו נקי ויעזור לנו להתמקד בבעיה שאנחנו מעוניינים לפתור.
  3. מִחזוּר: מודול מאפשר לנו להשתמש בקוד שכתבנו בפרויקטים רבים, מבלי לכתוב את הקוד מחדש בכל פרויקט.
  4. רמת גימור: המודולים הרשמיים של פייתון מתוחזקים ברמה גבוהה, יש בהם מעט באגים והם מכסים מקרי קצה רבים.

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

תרגיל ביניים: יצירת סיסמה

איש דג החרב מגדיר "מחולל סיסמאות חזקות" כך:

  1. בקריאה לפונקציית המחולל, המחולל יחזיר סיסמה חזקה.
  2. אורכה של סיסמה חזקה הוא בין 12 ל־20 תווים.
  3. לפחות בחלק מהסיסמאות שמייצר המחולל יהיו גם אותיות גדולות, גם אותיות קטנות וגם מספרים.

כתבו מחולל סיסמאות חזקות.

על אף שזהו תרגיל נחמד, לעולם לא נשתמש ב־random לצורכי אבטחת מידע.
להרחבה, קראו על יתרונותיו של המודול secrets.

יבוא מתוך מודול

עצרת של המספר $n$ (המסומנת כ־$n!$) היא מכפלת כל המספרים השלמים החיוביים עד $n$, כולל.
נחשב את העצרת של 6 באמצעות המודול math:


In [ ]:
import math
print(math.factorial(6))  # 1 * 2 * 3 * 4 * 5 * 6

במקרה שהצגנו, אין לנו באמת צורך בכל הפונקציות הנמצאות במודול math, אלא רק בפונקציה factorial.
תעלול נחמד שאפשר לעשות הוא לייבא רק את factorial באמצעות מילת המפתח from:


In [ ]:
from math import factorial
print(factorial(6))

כדאי לשים לב שלאחר שייבאנו בעזרת from נשתמש ישירות ב־factorial, מבלי להזכיר את השייכות שלה למודול math.
אפשר גם לייבא יותר משם אחד מאותו מודול, במכה אחת:


In [ ]:
from math import cos, pi
print(cos(pi))

באופן כללי, העדיפו לייבא את המודול כולו ולא חלקים מתוכו.
זה יעזור לכם להימנע מיצירת מקרים מבלבלים כמו זה:


In [ ]:
from math import e

# (דמיינו הרבה קוד כאן)
print(e)  # ?לרוב הוא שם משתנה שתוכנו שגיאה, האם זה המקרה גם פה e ?מאיפה זה הגיע

אם ממשיכים בכיוון, פייתון מאפשרת לנו לעשות את הדבר המזעזע הבא:


In [ ]:
from math import *

יבוא בעזרת כוכבית יגרום לכך שכל מה שמוגדר במודול "יישפך" לתוך התוכנית שלנו.
זה יאפשר לנו להשתמש בכל התוכן של math בלי להזכיר את שם המודול:


In [ ]:
print(f"floor(5.5) --> {floor(5.5)}")
print(f"ceil(5.5)  --> {ceil(5.5)}")
print(f"pow(9, 2)  --> {pow(9, 2)}")
print(f"e          --> {e}")

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

  • זה מקשה על מתכנת שקורא את הקוד להבין איפה הוגדרו ceil, floor, e ו־pow.
  • אנחנו "מזהמים" את הסביבה שלנו בהגדרות רבות שלעולם לא נשתמש בהן.
  • חלק מהכלים שבהם תשתמשו בעתיד לא ידעו לזהות את השמות הללו, כיוון שלא ייבאתם אותם מפורשות.

שינוי שם ביבוא

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

נראה דוגמה לתוצר של ריצת תוכנית שכזו, שבה אנחנו משרטטים 100 ריבועים בהיסט של מעלה אחת בכל פעם.
אנחנו ממליצים לשחק עם המודול קצת (זה כיף!) ולראות אם אתם מצליחים לצייר כוכב, לדוגמה :)


In [ ]:
import turtle

turtle.speed(10)

for i in range(400):
    turtle.forward(50 + i)
    turtle.right(91)

turtle.done()

הרצת קוד של turtle תפתח לכם חלון חדש בו יצויר פלט התוכנית.
כדי להמשיך להריץ את התאים במחברת, סגרו את החלון.

כדי לשנות את השם שבו אנחנו מתייחסים למודול, ניעזר במילת המפתח as.
לדוגמה:


In [ ]:
import turtle as kipik

kipik.speed(100)  # קיפיק מהיר הרבה יותר מצב רגיל

for i in range(400):
    kipik.forward(50 + i)
    kipik.right(91)

kipik.done()

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

ישנם שני מקרים יוצאי דופן, שבהם השימוש ב־as נחשב רצוי:

  1. כאשר מבקשים מאיתנו להשתמש ב־as במסמכי התיעוד של המודול.
  2. כאשר אנחנו רוצים להתנסות בדברים ולחסוך זמן, נקצר את השמות של המודולים, הפונקציות או הקבועים שאותם אנחנו מייבאים.

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

לשימוש במודולים יש כללי סגנון שמוסכמים על רוב המתכנתים.
הם מופיעים במסמך בשם PEP8, שמטרתו להגדיר איך נראה קוד פייתון המסוגנן כראוי.
הנה כמה כללים שראוי שתעקבו אחריהם:

  • יבוא המודולים תמיד יתבצע בראש הקוד, לפני כל דבר אחר.
  • כאשר מייבאים יותר ממודול אחד, סדר היבוא צריך להיות מילוני – לפי שם המודול שיובא.
  • טכנית, אפשר לייבא יותר ממודול אחד בשורה, אם נפריד את שמות המודולים בפסיק. מעשית זה לא מנומס.
  • כאשר משתמשים ב־from ליבוא של יותר משם אחד – השמות צריכים להיות מסודרים בסדר מילוני.
  • יש להימנע משימוש ביבוא בעזרת כוכבית.

דוגמאות נוספות

מה התאריך היום?


In [ ]:
import datetime
print("What is the time now?")
print(datetime.datetime.now())

נשתמש במודול calendar כדי להדפיס את לוח השנה של החודש הנוכחי:


In [ ]:
from calendar import prmonth as print_calendar  # לא מנומס, אבל דוגמה טובה
import datetime

current_date = datetime.datetime.now()
print_calendar(current_date.year, current_date.month)

סיכום

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

מונחים

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

תרגילים

בתרגילים הבאים השתמשו באינטרנט כדי למצוא מודולים ופונקציות שיסייעו לכם לפתור את התרגיל.
נסו להימנע מחיפוש ומקריאה של פתרונות לתרגיל המסוים המופיע במחברת (כמו חיפושים הכוללים "חפיסת קלפים").

זו הדרך

כתבו פונקציה שמקבלת נתיב לתיקייה, ומחזירה את רשימת כל הקבצים שמתחילים ברצף האותיות "deep" באותה תיקייה.
בדקו שהפעלת הפונקציה על התיקייה images מחזירה שני קבצים.

משחק קלפים משונה

בחפיסת קלפים רגילה, שבה 52 קלפים, יש לכל קלף שתי תכונות:

  1. ערך: מספר שבין 1 ל־13.
  2. צורה: תלתן (Club), יהלום (Diamond), לב (Heart) או עלה (Spade).

כל צירוף של ערך וצורה מופיע בחפיסה בדיוק פעם אחת.

  1. צרו חפיסת קלפים מלאה.
  2. טרפו את הקלפים.
  3. חלקו אותם בין 4 שחקנים.
  4. הדפיסו לאיזה שחקן סכום הקלפים הגבוה ביותר.

It's the final?

כתבו פונקציה שמקבלת תאריך עתידי בתצורה YYYY-MM-DD, ומדפיסה את מספר הימים שנשארו עד שנגיע לתאריך המיוחל.
לדוגמה, אם התאריך היום הוא 2020-05-04 וקיבלנו כקלט 2020-05-25, הפונקציה תחזיר 21.

אין לי וִנִגְרֶט

כתבו תוכנה שמקבלת כקלט מהמשתמש שני תאריכים בתצורה: YYYY-MM-DD.
התוכנה תגריל תאריך חדש שנמצא בין שני התאריכים שהמשתמש הזין כקלט.
לדוגמה, עבור הקלטים 1912-06-23 ו־1954-06-07, פלט אפשרי הוא 1939-09-03.
כיוון שאני הולך למכולת רק בימי שני ואני צרכן כבד של רוטב ויניגרט, אם התאריך נופל על יום שני, הדפיסו: "אין לי ויניגרט!"
רמז: קראו על EPOCH.