מה זה data classes בפייתון ואיך להשתמש בזה
data classes נוספו לפייתון גרסה 3.7. הם דומים לקלאסים רגילים של פייתון ומספקים תכונות שמקלות על עבודה עם תכנות מונחה עצמים עבור קלאסים שעיקר תפקידם להכיל מידע data classes.data classes עשויים להכיל לוגיקה מאוד פשוטה בזה הם שונים מקלאסים האחראים על התנהגות behavior-driven classes. דוגמה אחת ל-data class היא קלאס המכיל מידע על אדם - שם, גיל, גובה. דומה אחרת, היא קלאס המכיל מידע על קואורדינטות גיאוגרפיות. קלאס הכולל התנהגות יכול להצפין טקסט או לחשב את הבונוס השנתי.
נשתמש בדקורטור @dataclass כדי לכתוב קלאס מסוג data class. לדוגמה:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
height: float
- הגדרנו קלאס Person עם 3 שדות: שם, גיל וגובה. חובה להגדיר את סוג השדה (str, int, float)
בגלל שהקלאס הוא dataclass אין צורך להשתמש במתודה __init__ בשביל אתחול האובייקט מהקלאס.
ניצור אובייקט מהקלאס:
person = Person(name='Adi', age=25, height=1.65)
ועכשיו אנחנו יכולים לגשת למידע באובייקט:
print(person.name)
print(person.age)
print(person.height)
ערכים ברירת מחדל
לשדות השייכים לקלאס dataclass יכולים להיות ערכים ברירת מחדל. לדוגמה:
from dataclasses import dataclass
@dataclass
class Person:
name: str = ''
age: int = 0
height: float = 0.0
- ברירת המחדל של שדה שם הוא מחרוזת ריקה, הגיל 0, והגובה 0.0
אם ננסה ליצור אובייקט מהמחלקה בלי לספק ארגומנטים, הקלאס ישתמש בערכים ברירת מחדל במקום הערכים החסרים:
person = Person()
print(person.name) # prints ''
print(person.age) # prints 0
print(person.height) # prints 0.0
אם נספק רק חלק מהארגומנטים, הערכים ברירת המחדל ישמשו במקום אילו שהחסרנו:
person = Person(name='Adi', age=25)
print(person.name) # prints 'Adi'
print(person.age) # prints 25
print(person.height) # prints 0.0 (default value)
ערכים שניתן לקרוא אבל לא לשנות immutable
אובייקטים של dataclass יכולים להיות ניתנים לקריאה בלבד read-only מה שאומר שאי אפשר לערוך את המידע. כדי ליצור קלאסים מסוג זה נעביר את הפרמטר frozen לדקורטור. לדוגמה:
from dataclasses import dataclass
@dataclass(frozen=True)
class Person:
name: str
age: int
person = Person(name="Adi", age=25)
print(person.name) # prints 'Adi'
person.name = "Benny" # throws an error
- בדוגמה, הגדרנו את המידע באובייקטים כמי שניתן לקריאה בלבד. לא הייתה בעיה להגדיר את ערכי השדות בזמן ייסוד האובייקט מהקלאס.
- כשניסינו לערוך את השדה name אחרי יצירת האובייקט קיבלנו שגיאה כי המידע באובייקט הוא לקריאה בלבד.
כיוון שהמידע מוקפא לא נוכל לעשות מניפולציה של המשתנים לאחר ייסוד האובייקט. בדוגמה הבאה, ננסה להשתמש במתודה __post_init__ כדי להגדיר את השדה id לאחר ייסוד האובייקט מן הקלאס:
from dataclasses import dataclass
@dataclass(frozen=True)
class Person:
name: str
age: int
id: str = ''
def __post_init__(self):
# Generate an ID for the person
self.id = f"{self.name}-{self.age}"
person = Person(name="Adi", age=25)
# dataclasses.FrozenInstanceError: cannot assign to field 'id'
- התוצאה היא שגיאה כי לא ניתן לשנות את הערך של קלאס המיועד לקריאה בלבד.
כדי להגדיר את ערכו של שדה מתוך הקלאס לאחר ייסוד האובייקט נשתמש במתודה __setattr__ אותה נפעיל על האובייקט מתוך המתודה __post_init__:
from dataclasses import dataclass
@dataclass(frozen=True)
class Person:
name: str
age: int
id: str = ''
def __post_init__(self):
# Generate an ID for the person
new_id = f"{self.name}-{self.age}"
object.__setattr__(self, 'id', new_id)
person = Person(name="Adi", age=25)
print(person.id) # prints 'Adi-25'
איך להשוות בין אובייקטים?
אפשר להשוות אובייקטים של dataclass כיוון שהם כוללים את מתודת הקסם __eq__ המשמש להשוואה בין ערכי השדות של שני אובייקטים. לדוגמה:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
height: float
person1 = Person(name='Adi', age=25, height=1.65)
person2 = Person(name='Ben', age=30, height=1.8)
# Comparing two objects for equality:
if person1 == person2:
print("The two persons are equal")
else:
print("The two persons are not equal")
- בדוגמה לעיל יצרנו שני אובייקטים השייכים לסוג Person שיש להם ערכים שונים, ואז השתמשנו באופרטור == כדי להשוות ביניהם. כיוון שהאובייקטים מכילים מידע שונה התוצאה של הרצת הקוד היא "The two persons are not equal"
ניתן לדרוס את המתודה __eq__ ולהגדיר את הלוגיקה הדרושה לצורך השוואה. לדוגמה, להשוות רק חלק מהשדות במקום את כולם:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
height: float
def __eq__(self, other):
if isinstance(other, Person):
return self.name == other.name and self.age == other.age
return False
person1 = Person(name='Alice', age=25, height=1.65)
person2 = Person(name='Alice', age=25, height=1.8)
# Comparing two objects for equality:
if person1 == person2:
print("The two persons are equal")
else:
print("The two persons are not equal")
- בדוגמה לעיל דרסנו את המתודה __eq__ כדי שהשוואה תהיה רק על שם וגיל תוך התעלמות מהגובה. התוצאה של הקוד היא "The two persons are equal" כיוון שהשדות name ו-age של האובייקטים person1 ו-person2 הם שווים.
ייצוג של אובייקט באמצעות מחרוזת
את הקלאסים של פייתון ניתן להגדיר באמצעות מחרוזת על ידי יישום מתודת הקסם __str__ בקלאס. המתודה __str__ נקראת אוטומטית כשאתה מדפיס את האובייקט באמצעות print. לדוגמה:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
height: float
person = Person(name='Adi', age=25, height=1.65)
print(person) # prints "Person(name='Adi', age=25, height=1.65)"
- בדוגמה לעיל יצרנו אובייקט של הקלאס Person ואחר כך הדפסנו, וקיבלנו ייצוג קריא של האובייקט.
- אם היינו עושים את אותו דבר על אובייקט שמקורו בקלאס רגיל של פייתון היינו מקבלים הדפסה של המקום בזיכרון בו נשמר האובייקט.
אתה יכול לשנות את הייצוג באמצעות מחרוזת על פי צרכיך על ידי שינוי המחרוזת המפורמטת במתודה __str__. לדוגמה, להשמיט חלק מהשדות או להוסיף מרווחים.
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
height: float
def __str__(self):
return f"Person name is {self.name}, age {self.age}, height {self.height}m"
person = Person(name='Adi', age=25, height=1.65)
print(person) # prints "Person name is Adi, age 25, height 1.65m"
סריאליזציה של נתונים
סריאליזציה serialization היא תהליך הפיכת המידע שמכיל האובייקט למידע שניתן להזרימו דרך רשת האינטרנט או להופכו לנוח לקריאה על ידי בני אדם. דיסריאליזציה deserialization היא התהליך ההפוך אשר הופך מידע סריאלי חזרה לאובייקט.
נראה דוגמה לסריאליזציה באמצעות המודול json של פייתון:
import json
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
person = Person(name="Adi", age=25)
# serialize the object to a JSON string
json_str = json.dumps(person.__dict__)
# deserialize the JSON string to an object
person_dict = json.loads(json_str)
person = Person(**person_dict)
print(person) # prints "Person(name='Adi'', age=25)"
- בדוגמה, הגדרנו Person שהוא קלאס מבוסס data class עם השדות name ו-age.
- הקוד יצר אובייקט מהקלאס, הפך את האובייקט למחרוזת JSON באמצעות json.dumps אשר עובדת על המילון שיצרה המתודה __dict__ שהיא מתודת קסם שמספק הקלאס.
- בהמשך, הקוד עשה דיסריאליזציה מ-JSON למילון באמצעות json.loads ואז ייצר מהמילון אובייקט חדש של הקלאס Person.
שים לב, זו דוגמת שימוש אחת בלבד. ישנם שימושים רבים לסריאליזציה של נתונים לפורמטים שונים באמצעות מגוון ספריות של פייתון בהם ניתן להיעזר במידת הצורך.
לסיכום
data classes מיועדים לפתור כמה בעיות נפוצות שמתכנתים מתמודדים איתם כשהם עובדים על קוד של קלאסים המיועדים להכיל מידע. להלן חלק מהבעיות הנפוצות ביותר ש-data classes פותרים:
- הפחתת הצורך להשתמש בקוד boilerplate. כשאתה כותב בסגנון מונחה עצמים אתה נאלץ לכתוב יותר מדי קוד כדי להגדיר את השדות, את הקונסטרקטור ומתודות קסם דוגמת __eq__ ו-__str__. תהליך הכתיבה הוא מייגע ומועד לטעויות. data classes מתמודדים עם הבעיה על ידי כך שהם מייצרים מתודות שימושיות באופן אוטומטי, ומפחיתים את כמות הקוד שיש לכתוב.
- קריאות: קשה לקרוא ולהבין קלאסים ארוכים ומורכבים. data classes מספקים תחביר פשוט וקריא להגדרת קלאסים הודות ליצירה אוטומטית של מתודות והסתרת קוד boilerplate.
- יצירת אובייקטים בלתי ניתנים לשינוי immutable objects. אובייקטים של פייתון ניתנים לשינוי כברירת מחדל אחרי שיוצרים אותם. זה עלול לגרום לבאגים ולהתנהגות בלתי צפויה. data classes מקלים על יצירת אובייקטים שמרגע ייסודם אי אפשר לשנות אותם immutable objects על ידי כך שהם שהם כוללים מתודה __setattr__ שזורקת שגיאה כשמנסים לשנות מידע אחרי ייסוד האובייקט.
- סריאליזציה : כשרוצים להפוך מידע למחרוזת שניתן להדפיס בקובץ או לפורמט שניתן להזרים ברשת, קשה לכתוב את הקוד לצורך סריאליזציה ודיסריאליזציה. data classes מקלים את המלאכה על ידי יצירת מתודה __dict__ שמכיל מילון של שדות האובייקט אותם קל להפוך לפורמטים JSON, YAML וכיו"ב.
בסך הכול, data classes של פייתון מספקים כלי פשוט לשימוש ורב עוצמה ליצירה של קלאסים, ועל ידי כך הם מפחיתים את הצורך בכתיבת קוד boilerplate, משפרים את הקריאות, ומקלים את העבודה עם אובייקטים immutable ועם סריאליזציה של מידע.
מדריכים נוספים בסדרה ללימוד פייתון
למה ואיך לעבוד עם סביבה וירטואלית בפייתון
גירוד דפי רשת (Web scraping) באמצעות פייתון
לכל המדריכים בסדרה ללימוד פייתון
אהבתם? לא אהבתם? דרגו!
0 הצבעות, ממוצע 0 מתוך 5 כוכבים
המדריכים באתר עוסקים בנושאי תכנות ופיתוח אישי. הקוד שמוצג משמש להדגמה ולצרכי לימוד. התוכן והקוד המוצגים באתר נבדקו בקפידה ונמצאו תקינים. אבל ייתכן ששימוש במערכות שונות, דוגמת דפדפן או מערכת הפעלה שונה ולאור השינויים הטכנולוגיים התכופים בעולם שבו אנו חיים יגרום לתוצאות שונות מהמצופה. בכל מקרה, אין בעל האתר נושא באחריות לכל שיבוש או שימוש לא אחראי בתכנים הלימודיים באתר.
למרות האמור לעיל, ומתוך רצון טוב, אם נתקלת בקשיים ביישום הקוד באתר מפאת מה שנראה לך כשגיאה או כחוסר עקביות נא להשאיר תגובה עם פירוט הבעיה באזור התגובות בתחתית המדריכים. זה יכול לעזור למשתמשים אחרים שנתקלו באותה בעיה ואם אני רואה שהבעיה עקרונית אני עשוי לערוך התאמה במדריך או להסיר אותו כדי להימנע מהטעיית הציבור.
שימו לב! הסקריפטים במדריכים מיועדים למטרות לימוד בלבד. כשאתם עובדים על הפרויקטים שלכם אתם צריכים להשתמש בספריות וסביבות פיתוח מוכחות, מהירות ובטוחות.
המשתמש באתר צריך להיות מודע לכך שאם וכאשר הוא מפתח קוד בשביל פרויקט הוא חייב לשים לב ולהשתמש בסביבת הפיתוח המתאימה ביותר, הבטוחה ביותר, היעילה ביותר וכמובן שהוא צריך לבדוק את הקוד בהיבטים של יעילות ואבטחה. מי אמר שלהיות מפתח זו עבודה קלה ?
השימוש שלך באתר מהווה ראייה להסכמתך עם הכללים והתקנות שנוסחו בהסכם תנאי השימוש.