6 min. read

זהו המשך לפרקי ה-Decoratorים.
פרקים קודמים:

פייתון 20 - פונקציות קישוט חלק א פייתון 21 - פונקציות קישוט חלק ב

מה נלמד

  • Data classes
  • Enum
  • Flag

Data class

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

1
2
3
4
class Person:
def __init__(self, name, age):
self.Name = name
self.Age = age

עקרון תכנות שפות עיליות:

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

בנאים זה משהו שחוזר על עצמו - פונקציה שמקבלת פרמטרים ומעבירה אותם לבפנים.

כל דבר שחוזר על עצמו ניתן לאגד ליחידה לוגית.

לכן dataclass נוצר!

מה זה

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from dataclasses import dataclass

@dataclass
class Person:
Name: str = ''
Age: int = 0

# בנאי - המרה למחרוזת!
p = Person('Bob', '37')
print(p)

p2 = Person('Alice','28')
p3 = Person('Alice','28')
print(p == p2)
print (p2 == p3)

dataclass זה קישוט למחלקות שמקנה לו יכולות נוספות כמו:

  • בנאי
  • השוואות (אופרטורים כגון ==, !=, >, < וכדו’)
  • המרה למחרוזת

כל זה עם שורת קוד אחת! מדהים.

שמתם לב שניתן לשים קישוטים גם למחלקות?
זהו אחד החוזקות של דקורטורים!

הצעה PEP-557

PEP557

ההצעה הזו מסבירה יותר לעומק מה קורה מאחורי הקלעים:
בפועל השורה הקטנה הזו:
@dataclass

יוצרת את המתודות הבאות מאחורי הקלעים:

  • _init_ - בנאי
  • _repr_ - המרה למחרוזת
  • _eq_ - שווה
  • _ne_ - לא שווה
  • _lt_ - קטן מ
  • _le_ - קטן שווה ל
  • _gt_ - גדול מ
  • _ge_ - גדול שווה ל

תרגיל

שימו ❤:
לא צריך לשים ``@property``.
  1. כתבו מחלקות dataclass ל:
  • אדם
  • מוצר חשמלי
  • רהיט
  • צורה
  • לקוח

חשבו על 3 תכונות (מאפיינים) לכל אחד מהמודלים הללו.

  1. יצרו את היררכיית מחלקות המודלים הבאה:
  • חיה
  • כלב
  • האסקי
  • כלב שועלי



Enum

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

דוגמא א:
סוג שגיאה לקוד מספרי:

1
2
3
4
שגיאת המרה - 1
שגיאת קלט - 2
שגיאת קובץ - 3
וכדו'...

דוגמא ב:
צבע למספר:

1
2
3
4
5
כחול - 1
ירוק - 2
אדום - 3
סגול -4
וכדו'...

בפייתון

1
2
3
4
5
6
7
8
9
10
11
from enum import Enum

class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3

colorNum = int(input('Type color 1,2,3 to choose: '))
chosenColor = Color(colorNum)

print(f'You chose {chosenColor}')

משתמשים במודול enum.
יורשים את מחלקת Enum(שימו לב לאותיות גדולות).
יוצרים כמשתנים במחלקה את הערכים.

תרגיל

  1. ממשו את הדוגמא לקוד שגיאה כ-enum בפייתון.

יצירה

כדי להשתמש ב-enum שיצרנו ניתן לקרוא לו ישירות בשמו:

1
color = Color.RED

או להמיר מספר לערך שלו:

1
color = Color(1)

לקרוא את הערכים

עבור כל ערך שיצרנו ב-enum ניתן לקרוא אותו כרשימה:

1
2
for color in Color:
print(color)

תוצאה:

1
2
3
Color.RED  
Color.GREEN
Color.BLUE

שם טקסטואלי וערכו

כמו שציינו ה-enum משלב גם ערך מספרי וגם טקסט - השם של הערך.

ניתן לגשת לערך המספרי ע”פ השם של הערך:

1
2
redColor = Color['RED']
blueColor = Color['GREEN']

כמו כן לכל ערך יש שני משתנים שניתן לקרוא מהם:

  • name
  • value
1
2
3
4
redColor = Color['RED']

print(redColor.name)
print(redColor.value)

תוצאה:

1
2
RED
1

בעיית הייחודיות

מה מונע מאיתנו להוסיף צבע עם ערך נוסף?

1
2
3
4
5
6
7
from enum import Enum

class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
PURPLE = 3

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

מה ייקרה פה?

1
purple = Color(3)

unique

בשביל לפתור את הבעיה הזו ניתן להשתמש בדקורטור @unique.

1
2
3
4
5
6
7
8
from enum import Enum, unique

@unique
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
PURPLE = 3

מהות הדקורטור הוא להוסיף או לשנות התנהגות מבלי לפגוע בהתנהגות קיימת.

בעזרת unique ניתן לפתור את הבעיה הזו.

מה קורה ב-vs code כאשר אני מריץ את הקוד:

auto

ניתן לומר לקוד לייצר לנו את הערכים לבד בעזרת auto וכך למנוע השמה שגויה.

1
2
3
4
5
6
7
8
9
10
from enum import Enum, auto, unique

@unique
class Color(Enum):
RED = auto()
GREEN = auto()
BLUE = auto()
PURPLE = auto()

print(Color.PURPLE.value)

מדפיס:

1
4

דגלים וערכים מורכבים

Flag הוא מושג המגדיר “דגל” אשר אומר אם משהו “דלוק” או “לא דלוק”.
ממש כמו משתנה בוליאני!
אך הערך שלו הוא מספרי ולא True או False.

מה שFlag משתמש בו נקרא ביט.
ביט הוא הערך הקטן ביותר בתוכנה והוא מגדיר שני ערכים בלבד - 0 או 1.
דלוק או לא דלוק.

בוליאנים ומספרים - מאחורי הקלעים

לרענון הידע על בינארי מוזמנים לחזור לפרק 12:

פייתון 12 - קבצים בינאריים

ביט הוא הערך הקטן ביותר - מייצג 0 או 1.
בייט מורכב מ-8 ביטים.

המחשב לא יודע להקצות רק ביט אחד - הוא יודע לעבוד עם בייטים.
למען האמת רוב מערכות ההפעלה אפילו לא עובדות עם בייטים הם עובדים עם משהו שנקרא Page.
במערכות שונות הערך הזה יכול להיות שונה, אבל ברוב מחשבי ווינדוס הכמות זיכרון הכי קטנה שהמערכת נותנת היא 4 KiloBytes.
ז”א 4,096 בייטים או 32,768 ביטים.

המערכת נותנת את הכמות זיכרון הזו לתוכנה שלנו - קוד הפייתון,
והקוד יודע להקצות משתנים קטנים יותר.

ההבדל בין בוליאני למספר

בגלל שאין לנו יכולת לנהל ביט בודד אזי הקוד מקצה לנו בייט למשתנה שכל המטרה שלו זה 0 או 1.

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

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

בבייט אחד יש 8 דגלים.
זו לא רשימה לכן אני לא מתחיל מ-0.
אם נדליק את הדגל הראשון:

אפשר להדליק 4 דגלים בו זמנית:

כל דגל כזה מייצג לנו ביט - דלוק או לא דלוק.
כל דגל שדלוק יהיה 1 וכל דגל שכבוי יהיה 0:

נקבל את המספר הבינארי: 00001111.

נשים את המספר הזה במחשבון ונראה איזה מספר דצימלי נקבל:

אז כמות הדגלים הזו הביאה לנו את המספר 15.
למספר הזה אין באמת ערך עבורנו אבל ממנו אנחנו יכולים לדעת איזה דגלים דלוקים.

חשוב לדעת מהו הערך של כל ביט במספר דצימלי לפי מיקום הביט במספר, זה די פשוט:
במקום ה-1 לביט יש את הערך 1.
במקום ה-2 לביט יש את הערך 2.
במקום ה-3 לביט יש את הערך 4.
במקום ה-4 לביט יש את הערך 8.
מהו הערך בביט ה-5?
16!

כפי שאתם רואים אלו כפולות של שניים מכיוון שמתמשים בבסיס בינארי - 2!
המחשב אוהב כפולות של 2.
לכן זה ימשיך ככה…
1, 2, 4, 8, 16, 32, 64, 128, 256 וכדו’…

בפייתון

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

1
2
3
4
5
6
7
8
9
10
11
12
from enum import auto, Flag

class CarOptions(Flag):
HasNitro = auto()
HasSideWings = auto()
HasGun = auto()
HasArmor = auto()
Protected = HasGun | HasArmor
Warrior = HasGun | HasNitro | HasSideWings

print(CarOptions.HasNitro.value)
print(CarOptions.Warrior.value)

האופרטור | הוא פעולת OR בין שני מספרים.
זוכרים OR?

פייתון 2 - משתנים

לפני שאתם מריצים את הקוד - מה יודפס בשני הערכים האלו?

_contains_

על מנת לבדוק אם דגל דלוק ניתן להשתמש ב-__contains__.

1
2
options = CarOptions.Warrior
hasNitro = options.__contains__(CarOptions.HasNitro)

תרגיל

1.
כתבו Enum המייצג את השלב שבו חבילת דואר נמצאת.
תחשבו על איזה מצבים החבילת דואר תהיה בהן.

  1. בסעיף הזה אשלב משהו מעט יותר מורכב - דגלי TCP.
    ניתן לקרוא עליהם כאן

לא חייב להבין מה הם עושים - אך נסו לממש מחלקה שמכילה את כל הדגלים.




בפרק הבא נלמד לעומק יותר על Iteratable ו-Generators:

פייתון 23 - ג'נרטורים

אהבתם? מוזמנים להביע תמיכה כאן: כוס קפה