פייתון

טריקים שכל מפתח Python חייב להכיר – חלק ב'

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

אינדקס שלילי

שפת פייתון מאפשר גישה לאיבר במיקום ה-x מסוף הרשימה ע"י שימוש באינדקס שלילי. לדוגמה:

my_list[-1]

יחזיר את האיבר האחרון ו-

my_list[-3]

יחזיר את האיבר השלישי מהסוף.

defaultdict ו-Counter

כנראה שאתם כבר מכירים את המילון (dict), אבל האם ידעתם ש-Python מספקת גם מילון עם ערכי ברירת מחדל? לדוגמה:

from collections import defaultdict
groups = defaultdict(list)
groups["fruits"].append("banana") // groups["fruits"] = ["banana"]
print groups["vegetables"] // []

מבנה נתונים דומה הוא Counter, המזכיר את defaultdict(int) אך מספק מספר פונקציות שימושיות עבור מניה של אובייקטים:

from collections import Counter
counter = Counter()
for char in "banana":
  counter[char] += 1
print counter.most_common() // [('a', 3), ('n', 2), ('b', 1)]

Interactive Debugging

אם אתם כמוני – משתמשים בעורך טקסט כדי לערוך קוד פייתון (ולא ב-IDE), ומריצים את התוכנית באמצעות ה-command line, בטח יצא לכם להוסיף באופן זמני פקודות print כדי "לדבג" את הקוד ולהדפיס מידע שימושי. במקרים רבים זה עובד, אבל הבעיה עם השיטה הזאת היא שאם רוצים להדפיס מידע נוסף, יש צורך לערוך את הקוד ולהריץ מחדש את התוכנית.

שיטה נוספת ולעתים נוחה יותר לביצוע דיבאג היא שימוש בדיבאגר המובנה של python ששמו הוא, באופן לא מפתיע, Python Debugger, ובקיצור PDB. באמצעות הוספת שורה אחת לקוד שלכם, תוכלו לעצור את הקוד בזמן ריצה, ולקבל python shell בתור ה-context הנוכחי של התוכנית שלכם, ממנו תוכלו לגשת למשתנים ולהריץ כל קוד Python שבא לכם:

import pdb; pdb.set_trace()

כמו לכל דיבאגר, גם ל-PDB יש פקודות שונות עליהן ניתן לקרוא כאן.

הפונקציה partial

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

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

def round3(n):
    return round(n, 3)

>> round3(3.141592653)
3.142

הפונקציה partial מאפשרת לנו להגיע לאותה תוצאה בשורה אחת:

round3 = partial(round, ndigits = 3)

יתרונותיה של שיטה זאת הם שאין צורך להכיר או להתייחס לשאר הפרמטרים של הפונקציה המקורית (שימו לב שלא התייחסנו ל-n בכלל), ושהתחביר פשוט וקריא.

המודול itertools

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

  • הפונקציה chain משרשרת מספר איטרטורים לאיטרטור אחד, שכאשר רצים עליו, פשוט רץ על כל האיטרטורים אחד אחרי השני:
chain('ABC', xrange(3)) // A B C 0 1 2
  • הפונקציה product מחזירה מכפלה קרטזית של מספר איטרטורים. למשל עבור שני איטרטורים היא תחזיר את אוסף כל הזוגות המורכבים מאיבר מהאיטרטור הראשון ואיבר מהאיטרטור השני, בדומה לשתי לולאות מקוננות:
product("AB", "12") // ('A', '1'), ('A', '2'), ('B', '1'), ('B', '2')
  • הפונקציה izip דומה לפונקציה המובנית zip (אין קשר לדחיסת נתונים, אלא ל"ריץ' רץ'"), אבל מחזירה איטרטור במקום רשימה. שימושית כאשר רוצים לרוץ על שני איטרטורים או יותר המכילים מספר רב של ערכים, בלי לטעון את כל המידע לזיכרון בבת אחת.
  • פונקציות מעניינות נוספות: combinations, permutations, groupby, cycle.

Pylint

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

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

> pip install pylint
> pylint --reports=n myapp.py
F:  5, 1: Unable to import 'non.existing' (import-error)

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

המודול logging

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

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

import logging
...
logging.warning("Invalid parameter %s", param)

מעבר להדפסת הלוגים, המודול מאפשר לנו להגדיר בצורה נוחה ומרוכזת איך תיראה כל הודעת לוג, לאן לכתוב את הלוג, ואילו סוגי הודעות להציג ומתי (למשל הודעות debug להציג רק כאשר רצים בסביבת הפיתוח). להלן דוגמה בסיסית לכתיבת כל ההודעות ברמה info ומעלה לקובץ:

import logging.config
logging.config.dictConfig({
  'version': 1,
  'formatters': { 
    'standard': { 
      'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
    },
  },
 'handlers': { 
    'file': { 
      'formatter': 'standard',
      'class': 'logging.FileHandler',
      'filename': 'myapp.log',
    },
  },
  'loggers': { 
    '': { 
      'handlers': ['file'],
      'level': 'INFO',
      'propagate': True,
    },
  },
})

4 תגובות בנושא “טריקים שכל מפתח Python חייב להכיר – חלק ב'”

  1. כמפתח פייתון הרבה שנים תמיד כיף להזכר בדברים הקטנים שיכולים לעזור, תודה רבה ותמשיך עם הבלוגים האלה !:-)

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים