GammaCyber

OOP חלק א׳ — Classes ו-Encapsulation

תכנות, אלגוריתמיקה ו-Web · 5–6 שעות

// סקירה

OOP חלק א׳ — Classes, Objects ו-Encapsulation

שיעור זה הוא הכי קריטי לסדנה. OOP אוגד נתונים ופעולות לתוך ישות אחת — Class. במקום 3000 משתנים לניהול 1000 סטודנטים, כותבים class אחד.

✓נושאי השיעור
  • Class ו-Object — blueprint vs instance, type(), isinstance()
  • __init__ Constructor — אתחול attributes, ברירות מחדל
  • Class Attributes vs Instance Attributes — משותפים vs ייחודיים
  • Encapsulation — public/protected/private, Name Mangling
  • Properties — @property, @setter, computed read-only properties
  • Method types — instance, @classmethod, @staticmethod
  • Magic Methods (Dunder) — __str__, __repr__, __len__, __eq__, __add__
  • UML Class Diagram — קריאה וכתיבה, סמלים +/-/#
  • Python vs C# — טבלת השוואה מקיפה
🎯 קשר למיון גאמא סייבר
סדנה מעשית: תרגיל OOP — כתוב class system שלם ב-C# או Python. שאלון: "מה זה encapsulation?", "מה ההבדל בין class method ל-static method?" ראיון: "הראה לי class שכתבת" — חייב שליטה מלאה.
// פרק 1

Class ו-Object: הרעיון הבסיסי

Class = תבנית (blueprint). Object = המופע (instance) הספציפי. class Dog מגדיר מבנה; rex = Dog("Rex") יוצר object.

__init__: ה-Constructor

__init__ נקראת אוטומטית בעת יצירת Object. self = ה-object הנוכחי (כמו this ב-C#).

class Student: def __init__(self, name, age, grades=None): self.name = name # instance attribute self.age = age # instance attribute self.grades = grades if grades is not None else [] def get_average(self): if not self.grades: return 0 return sum(self.grades) / len(self.grades) # יצירת instances alice = Student("Alice", 20, [90, 85, 92]) bob = Student("Bob", 22) # grades=[] כברירת מחדל print(alice.name) # Alice print(alice.get_average()) # 89.0 print(bob.get_average()) # 0 print(type(alice)) # <class '__main__.Student'> print(isinstance(alice, Student)) # True print(alice is bob) # False — שני objects שונים!
💡 מה זה self?
self הוא הפרמטר הראשון של כל instance method. Python מעבירה אותו אוטומטית — לא צריך לשלוח אותו בקריאה.
alice.get_average() = Python מעבירה Student.get_average(alice). self = alice.
הרחבה C# — אותו רעיון ›
🔷 C# Constructor
public class Student { public string Name { get; set; } public int Age { get; set; } public List<int> Grades { get; set; } // Constructor (מקביל ל-__init__) public Student(string name, int age) { Name = name; Age = age; Grades = new List<int>(); } public double GetAverage() { if (Grades.Count == 0) return 0; return Grades.Average(); } } // שימוש: var alice = new Student("Alice", 20); alice.Grades.Add(90); Console.WriteLine(alice.GetAverage()); // 90
✏תרגול עצמי3 שאלות
שאלה 1.1
מה ההבדל בין Class לבין Object?
✓ תשובה: א) — Class = blueprint (תכנית אדריכל). Object = הבית שנבנה לפיו. class Dog הוא התבנית; rex = Dog("Rex") הוא instance.
שאלה 1.2
class Dog: species = "Canis" def __init__(self, name): self.name = name d = Dog("Rex") print(d.species, d.name)מה הפלט?
✓ תשובה: ב) Canis Rex — species הוא class attribute — גישה דרך instance עובדת. d.species = "Canis". d.name = "Rex" (instance attribute).
שאלה 1.3
מה תפקיד __init__? מה המקבילה שלה ב-C#?
✓ תשובה: ד) — __init__ = Constructor. נקראת אוטומטית עם new Student(). המקבילה ב-C#: public Student(string name, int age) { Name=name; Age=age; }
🎯 דגש למבחן
הבנאי האמיתי בפייתון הוא __new__ ולא __init__. כשכותבים Dog("Rex") מתרחשים שני שלבים: __new__(cls, ...) הוא זה שיוצר ומקצה את האובייקט בזיכרון ומחזיר אותו, ורק אחריו __init__(self, ...) רץ כדי לאתחל את האובייקט שכבר קיים. לכן __init__ תמיד מחזיר None — הוא initializer, לא constructor. אם __new__ מחזיר אובייקט מטיפוס אחר, __init__ כלל לא יופעל. זו הנקודה שמאפשרת מימוש Singleton וטיפוסים בלתי-משתנים (int, str, tuple), ושאלה קלאסית במיון.
// פרק 2

Class Attributes vs Instance Attributes

Class Attributes — משותפים לכל ה-objects. Instance Attributes — ייחודיים לכל object (נכתבים ב-__init__ עם self).

class BankAccount: # Class Attributes — משותף לכל instances bank_name = "בנק גאמא" interest_rate = 0.03 total_accounts = 0 def __init__(self, owner, balance=0): # Instance Attributes — ייחודי לכל instance self.owner = owner self.balance = balance self.account_id = BankAccount.total_accounts + 1 BankAccount.total_accounts += 1 # עדכון class attribute def get_info(self): return f"[{self.account_id}] {self.owner}: ₪{self.balance}" acc1 = BankAccount("Alice", 1000) acc2 = BankAccount("Bob", 500) print(acc1.get_info()) # [1] Alice: ₪1000 print(acc2.get_info()) # [2] Bob: ₪500 print(BankAccount.total_accounts) # 2 print(acc1.bank_name) # בנק גאמא
! הפתעה: שינוי class attribute דרך instance ›
acc1.bank_name = "בנק פרטי" # ← יוצר INSTANCE attribute חדש! print(acc1.bank_name) # בנק פרטי (instance attribute) print(acc2.bank_name) # בנק גאמא (class attribute — לא השתנה!) # acc1 עכשיו יש לו instance attribute bank_name שמסתיר את ה-class attribute # שינוי class attribute נכון: תמיד דרך ClassName.attribute = ...
✏תרגול עצמי3 שאלות
שאלה 2.1
class Counter: count = 0 def __init__(self): Counter.count += 1 c1 = Counter() c2 = Counter() print(Counter.count)מה הפלט?
✓ תשובה: ג) 2 — כל יצירת instance מגדילה count ב-1. אחרי c1: count=1. אחרי c2: count=2. Counter.count = 2.
שאלה 2.2
class A: x = [] a = A() b = A() a.x.append(1) print(b.x)מה הפלט?
✓ תשובה: ב) [1] — x הוא class attribute שהוא list (mutable). a.x ו-b.x מצביעים לאותה list! a.x.append(1) משנה את הlist המשותפת → b.x = [1].
שאלה 2.3
class A: x = 5 a = A() a.x = 10 print(A.x, a.x)מה הפלט?
✓ תשובה: ד) 5 10 — a.x=10 יוצר instance attribute שמסתיר (shadows) את class attribute. A.x נשאר 5. a.x = 10 (instance).
🎯 דגש למבחן
Class Attribute מתכלה = דליפת זיכרון. Class attribute חי כל עוד המחלקה טעונה — כלומר לאורך כל חיי התהליך. אם הוא אובייקט משתנה (list, dict, set) וכל instance מוסיף אליו (MyClass.cache.append(self)), הרשימה רק גדלה, מחזיקה הפניות לאובייקטים ישנים, ומונעת מה-Garbage Collector לשחרר אותם — דליפת זיכרון קלאסית בשרתים ובסוכנים שרצים ימים. הכלל: מצב ששייך ל-instance נשמר ב-self בתוך __init__; class attribute שמור לקבועים ולמונים בלבד (ואם חייבים cache — עם הגבלת גודל או weakref).
// פרק 3

Encapsulation: הגנה על הנתונים

Encapsulation = מנגנון לשליטה על גישה לנתונים. ה-class חושף רק מה שצריך, ומסתיר את הפרטים הפנימיים. זה אחד מ-4 עמודות OOP.

רמהPythonC#גישה
Publicnamepublicכולם יכולים לגשת
Protected_nameprotectedה-class עצמו + ירושה
Private__nameprivateה-class בלבד
class SecureAccount: def __init__(self, owner, pin): self.owner = owner # public self._balance = 0 # protected — _: "אל תיגע" self.__pin = pin # private — __ name mangling def deposit(self, amount): if amount <= 0: raise ValueError("סכום חייב להיות חיובי") self._balance += amount def get_balance(self, pin): if not self.__verify_pin(pin): raise PermissionError("PIN שגוי!") return self._balance def __verify_pin(self, pin): # private method return self.__pin == pin acc = SecureAccount("Alice", "1234") acc.deposit(1000) print(acc.get_balance("1234")) # 1000 # ניסיון גישה לא נכון: # print(acc.__pin) # AttributeError! # print(acc._SecureAccount__pin) # עובד — Name Mangling, אבל רע!
הרחבה Name Mangling: איך Python ממש מסתירה ›

כשכותבים __name, Python שומרת זאת בפנים בתור _ClassName__name. זה מונע גישה מקרית, אבל לא מגנה באמת — Python עושה "soft" encapsulation.

class Demo: def __init__(self): self.__secret = 42 d = Demo() # d.__secret # AttributeError d._Demo__secret # 42 — Name Mangling חשוף! print(dir(d)) # רואים '_Demo__secret' ברשימה # בניגוד ל-C# שבה private אמיתי — # אי אפשר לגשת בשום דרך מחוץ ל-class

@property: getters ו-setters עם ולידציה

class Temperature: def __init__(self, celsius=0): self._celsius = None self.celsius = celsius # ← קורא ל-setter מיד @property def celsius(self): return self._celsius @celsius.setter def celsius(self, value): if value < -273.15: raise ValueError(f"{value}°C מתחת לאפס מוחלט!") self._celsius = value @property def fahrenheit(self): # read-only! אין setter return self._celsius * 9/5 + 32 t = Temperature(100) print(t.celsius) # 100 print(t.fahrenheit) # 212.0 # t.fahrenheit = 50 # AttributeError! — read-only try: t.celsius = -300 except ValueError as e: print(e) # -300°C מתחת לאפס מוחלט!
✏תרגול עצמי3 שאלות
שאלה 3.1
מה זה Name Mangling? לדוגמה, self.__x = 5 בclass A יישמר בפנים כ:
✓ תשובה: ג) _A__x — Name Mangling: __x → _ClassName__x. ניתן לגשת דרך a._A__x אבל לא דרך a.__x. Python לא מגנה באמת — שם שונה בלבד.
שאלה 3.2
מה @property עושה? למה להשתמש בו במקום לגשת ישירות ל-_celsius?
✓ תשובה: ב) — @property: הופך method לנראה כ-attribute (t.celsius ולא t.celsius()). @setter: מאפשר ולידציה לפני שמירה. computed properties מחושבים on-the-fly (fahrenheit).
שאלה 3.3
מה ההבדל בין Encapsulation ב-Python לבין ב-C#?
✓ תשובה: א) — Python: "We're all consenting adults here" — Name Mangling מקשה גישה אך לא מונעת. C#: private אמיתי — המהדר מונע גישה לגמרי.
🎯 דגש למבחן
הכמסה היא מוסכמה, לא אבטחה — Reflection עוקף הכול. ב-Python self.__x עובר Name Mangling ל-_ClassName__x — מי שיודע את השם פשוט ניגש אליו. ב-C# private נאכף על-ידי הקומפיילר, אבל ב-Runtime אפשר לעקוף אותו לגמרי עם Reflection: typeof(T).GetField("_balance", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(obj, 999) משנה שדה פרטי ישירות. המסקנה המקצועית: private מגן מפני טעויות של מתכנתים, לא מפני תוקף — ולכן סודות אמיתיים לא נשמרים בשדה פרטי.
// פרק 5

Methods: סוגי מתודות

יש 3 סוגי methods ב-Python. ההבדל: מי הפרמטר הראשון שלהם ומה הם יכולים לגשת.

סוגDecoratorפרמטר ראשוןגישה לשימוש
Instance Method(ללא)selfinstance + classהרוב הגדול של methods
Class Method@classmethodclsclass בלבדfactory methods, מונים
Static Method@staticmethod(ללא)לא לinstance ולא לclassutility functions
class Date: month_names = {1:"ינואר",2:"פברואר",3:"מרץ",4:"אפריל", 5:"מאי",6:"יוני",7:"יולי",8:"אוגוסט", 9:"ספטמבר",10:"אוקטובר",11:"נובמבר",12:"דצמבר"} def __init__(self, day, month, year): self.day, self.month, self.year = day, month, year # Instance Method — עובד עם self def to_string(self): name = Date.month_names.get(self.month, "?") return f"{self.day} ב{name} {self.year}" # Class Method — factory: יוצר instance בדרך חלופית @classmethod def from_string(cls, date_str): """יוצר Date מ-string בפורמט DD/MM/YYYY""" parts = date_str.split("/") d, m, y = int(parts[0]), int(parts[1]), int(parts[2]) return cls(d, m, y) # cls = Date (או תת-מחלקה!) # Static Method — לא צריך self או cls @staticmethod def is_valid_date(day, month, year): """בדיקת תקינות תאריך""" if year < 1 or month < 1 or month > 12 or day < 1: return False days_in_month = [0,31,28,31,30,31,30,31,31,30,31,30,31] return day <= days_in_month[month] # שימוש: d1 = Date(15, 3, 2024) print(d1.to_string()) # 15 במרץ 2024 d2 = Date.from_string("20/06/2024") # class method print(d2.to_string()) # 20 ביוני 2024 print(Date.is_valid_date(31, 2, 2024)) # False print(Date.is_valid_date(29, 3, 2024)) # True
✏תרגול עצמי3 שאלות
שאלה 5.1
מתי תשתמש ב-@staticmethod?
✓ תשובה: ג) — @staticmethod = utility function שלא צריכה שום context מה-class. כמו פונקציה רגילה שנמצאת ב-namespace של ה-class. דוגמה: Employee.is_valid_salary(50000).
שאלה 5.2
מה זה Factory Method?
✓ תשובה: ב) — @classmethod שמקבל cls ויוצר ומחזיר instance. מאפשר יצירת instances מ-string, מ-dict, ממקורות שונים — בלי להכביד על ה-constructor הראשי.
שאלה 5.3
מדוע to_string הוא instance method ואילו is_valid_date הוא static method?
✓ תשובה: א) — to_string משתמשת ב-self.day, self.month, self.year — מידע ספציפי ל-instance. is_valid_date רק בודקת שלושה ערכים — לא צריכה שום מידע מ-instance קיים.
// פרק 6

Magic Methods (Dunder Methods)

Magic Methods הן מתודות מיוחדות עם שמות כמו __str__, __len__. Python קוראת להן אוטומטית בתגובה לפעולות מסוימות.

Magic Methodמתי נקראתדוגמה
__init__יצירת objectobj = MyClass()
__str__print(obj), str(obj)ייצוג קריא למשתמש
__repr__repr(obj), ב-REPLייצוג debug מדויק
__len__len(obj)מחזיר "אורך"
__eq__obj1 == obj2שוויון מותאם
__lt__obj1 < obj2השוואה — מאפשר sorted()
__add__obj1 + obj2חיבור מותאם
__contains__item in objבדיקת in
__getitem__obj[key]indexing
__iter__for x in objאיטרציה
class Vector: """וקטור 2D — מדגים Magic Methods""" def __init__(self, x, y): self.x, self.y = x, y def __str__(self): return f"Vector({self.x}, {self.y})" def __repr__(self): return f"Vector(x={self.x}, y={self.y})" def __len__(self): return int((self.x**2 + self.y**2) ** 0.5) def __eq__(self, other): if not isinstance(other, Vector): return NotImplemented return self.x == other.x and self.y == other.y def __add__(self, other): return Vector(self.x + other.x, self.y + other.y) def __mul__(self, scalar): return Vector(self.x * scalar, self.y * scalar) def __lt__(self, other): return len(self) < len(other) # מאפשר sorted()! v1 = Vector(3, 4) v2 = Vector(1, 2) print(v1) # Vector(3, 4) print(repr(v1)) # Vector(x=3, y=4) print(len(v1)) # 5 (sqrt(9+16)=5) print(v1 == v2) # False print(v1 == Vector(3,4)) # True print(v1 + v2) # Vector(4, 6) print(v1 * 3) # Vector(9, 12) vectors = [Vector(3,4), Vector(1,1), Vector(5,12)] print(sorted(vectors)) # מסודר לפי magnitude!
הרחבה Magic Methods ב-Class שלם ›
class Stack: def __init__(self): self._data = [] self._max_size = 100 def push(self, item): if len(self._data) >= self._max_size: raise OverflowError("Stack מלא!") self._data.append(item) def pop(self): if self.is_empty(): raise IndexError("Stack ריק!") return self._data.pop() def peek(self): if self.is_empty(): raise IndexError("Stack ריק!") return self._data[-1] def is_empty(self): return len(self._data) == 0 def __len__(self): return len(self._data) def __str__(self): return f"Stack({self._data})" def __contains__(self, item): return item in self._data s = Stack() s.push(1); s.push(2); s.push(3) print(s) # Stack([1, 2, 3]) print(len(s)) # 3 print(s.peek()) # 3 (לא מוסיר) print(2 in s) # True print(s.pop()) # 3 (מוסיר ומחזיר) print(s) # Stack([1, 2])
✏תרגול עצמי3 שאלות
שאלה 6.1
מה ההבדל בין __str__ לבין __repr__?
✓ תשובה: ב) — __str__: מה print(obj) מציג — קריא למשתמש. __repr__: מה repr(obj) מציג — representation מדויק לdebug. אם __str__ לא מוגדר, Python משתמשת ב-__repr__.
שאלה 6.2
class Vec: def __init__(self, x): self.x = x def __add__(self, other): return Vec(self.x + other.x) def __str__(self): return str(self.x) v = Vec(3) + Vec(4) print(v)מה הפלט?
✓ תשובה: ג) 7 — Vec(3).__add__(Vec(4)) = Vec(3+4) = Vec(7). print(Vec(7)) קורא __str__ → str(7) = "7".
שאלה 6.3
sorted() על objects דורש שיהיה מוגדר Magic Method. מי הוא?
✓ תשובה: ג) __lt__ — sorted() דורש __lt__ (less than). מחזיר רשימה ממוינת לפי ה-ordering שהגדרת ב-__lt__. אפשר גם להשתמש ב-key= parameter.
// פרקים 7–8

UML Class Diagram ו-Python vs C#

UML מתאר מבנה מחלקות בצורה גרפית. בסדנה ובראיון, לפעמים מציגים UML ומצפים שתבין — או מבקשים לצייר.

סמל UMLמשמעותPythonC#
+Publicnamepublic
-Private__nameprivate
#Protected_nameprotected
«static»Static@staticmethodstatic
{readonly}Read-only property@property (ללא setter){ get; }

Python vs C# — טבלת השוואה מקיפה

תכונהPythonC#
Class הגדרהclass MyClass:public class MyClass { }
Constructordef __init__(self, ...):public MyClass(...) { }
this/selfself (מפורש)this (משתמע)
Publicname (ברירת מחדל)public type name;
Private__nameprivate type _name;
Property@propertypublic Type Name { get; set; }
Static@staticmethodstatic
Class Method@classmethodstatic (אין מקבילה מדויקת)
Inheritanceclass Child(Parent):class Child : Parent
Type hintsx: int (optional)int x (חובה)
הרחבה מערכת ניהול עובדים — Python וC# ›
# ─── Python ─────────────────────────────────────────── class Employee: company = "TechCorp" # class attribute _next_id = 1 def __init__(self, name: str, salary: float): self.name = name self._salary = salary # protected self.__id = Employee._next_id # private Employee._next_id += 1 @property def salary(self) -> float: return self._salary @salary.setter def salary(self, value: float): if value < 0: raise ValueError("שכר לא יכול להיות שלילי") self._salary = value @property def employee_id(self) -> int: # read-only return self.__id @classmethod def from_dict(cls, data: dict): return cls(data["name"], data["salary"]) @staticmethod def is_valid_salary(amount: float) -> bool: return 0 <= amount <= 1_000_000 def __str__(self) -> str: return f"[{self.__id}] {self.name} @ {self._salary:,.0f}₪" def __eq__(self, other) -> bool: return isinstance(other, Employee) and self.__id == other.__id emp1 = Employee("Alice", 15000) emp2 = Employee.from_dict({"name": "Bob", "salary": 12000}) print(emp1) # [1] Alice @ 15,000₪ emp1.salary = 16000 print(emp1.employee_id) # 1 (read-only)
✏תרגול עצמי3 שאלות
שאלה 7.1
ב-C#, מה ההבדל בין public Type Name {get;} לבין public Type Name {get; private set;}?
✓ תשובה: ד) — {get;} = read-only property (init-only). {get; private set;} = קריאה מכולם, כתיבה רק מתוך ה-class. שקול ל-@property עם setter private ב-Python.
שאלה 7.2
מה "Duck Typing" ב-Python?
✓ תשובה: ב) — "If it walks like a duck and quacks like a duck, it's a duck." Python לא בודקת type — רק אם יש את המתודות הנדרשות. לכן אין interface חובה ב-Python.
שאלה 7.3
ב-C# 9+, מה ההבדל בין var emp = new Employee(); לבין Employee emp = new Employee();?
✓ תשובה: ד) — ב-C# 9+, var = type inference. המהדר מסיק את הטיפוס מהצד הימני. שתי השורות זהות לחלוטין, var לא שקול ל-dynamic.
// אתגר מעשי

אתגר מעשי — OOP: מחלקות ו-Magic Methods

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

T2 · בסיסי250 נק'~20 דק'algorithmic
Vector2D עם Magic Methods
ממש מחלקה Vector2D המייצגת וקטור דו-ממדי עם x,y. עליה לתמוך ב: חיבור וקטורים באמצעות + (__add__), השוואת שוויון עם == (__eq__), ייצוג טקסטואלי repr בפורמט המדויק 'Vector2D(x, y)' (__repr__), ומתודה magnitude() המחזירה את גודל הווקטור (שורש סכום הריבועים).
דוגמאות
Vector2D(1,2) + Vector2D(3,4) → Vector2D(4, 6)
repr(Vector2D(4, 6)) → 'Vector2D(4, 6)'
Vector2D(3,4).magnitude() → 5.0
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס Vector2D עם Magic Methods ›
class Vector2D: def __init__(self, x, y): self.x = x; self.y = y def __add__(self, o): return Vector2D(self.x + o.x, self.y + o.y) def __eq__(self, o): return isinstance(o, Vector2D) and self.x == o.x and self.y == o.y def __repr__(self): return f"Vector2D({self.x}, {self.y})" def magnitude(self): return (self.x**2 + self.y**2) ** 0.5
T3 · בינוני350 נק'~25 דק'algorithmic
BankAccount — אינקפסולציה וחריגות
ממש מחלקה BankAccount עם יתרה מוסתרת (name mangling). דרישות: __init__(balance=0) — יתרה שלילית זורקת ValueError. deposit(amt) ו-withdraw(amt) — סכום ≤ 0 זורק ValueError; משיכה מעבר ליתרה זורקת ValueError. balance חשוף כ-property לקריאה בלבד (אי אפשר לבצע account.balance = X מבחוץ).
דוגמאות
a=BankAccount(100); a.deposit(50); a.balance → 150
a.withdraw(30); a.balance → 120
a.withdraw(1000) → ValueError
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס BankAccount — אינקפסולציה וחריגות ›
class BankAccount: def __init__(self, balance=0): if balance < 0: raise ValueError("balance must be >= 0") self.__balance = balance # שם-משתנה מוסתר (name mangling) @property def balance(self): # קריאה בלבד — אין setter return self.__balance def deposit(self, amt): if amt <= 0: raise ValueError("amt must be > 0") self.__balance += amt def withdraw(self, amt): if amt <= 0: raise ValueError("amt must be > 0") if amt > self.__balance: raise ValueError("insufficient funds") self.__balance -= amt
// סיכום

סיכום השיעור

✓מה למדנו בשיעור זה
  • Class ו-Object: blueprint vs instance, self, type(), isinstance()
  • __init__ Constructor: אתחול attributes, ברירות מחדל
  • Class Attributes vs Instance Attributes: משותפים vs ייחודיים
  • Encapsulation: public/protected/private, Name Mangling, Python vs C#
  • Properties: @property, @setter, computed read-only properties
  • Method types: instance, @classmethod, @staticmethod
  • Magic Methods: __str__, __repr__, __len__, __eq__, __add__, __iter__
  • UML Class Diagram: קריאה וכתיבה, סמלים +/-/#
  • Python vs C#: טבלת השוואה, Properties, Static methods
🎯 השיעור הבא
שיעור 8 — OOP חלק ב׳: Inheritance, Polymorphism, Design Patterns
לפני שיעור 8 — ודא שאתה יכול לכתוב Employee class מהראש ב-5 דקות. Encapsulation + Properties + Magic Methods — ידועים בעל-פה.
✏תרגול סיום — גאמא סגנון3 שאלות
גאמא סגנון א׳
class Robot: total = 0 def __init__(self, name, power): Robot.total += 1 self.name = name self.power = power self.id = Robot.total def __str__(self): return f"Robot#{self.id}:{self.name}(p={self.power})" def upgrade(self, boost): self.power += boost return self r1 = Robot("Alpha", 10) r2 = Robot("Beta", 20) r1.upgrade(5).upgrade(5) print(Robot.total) print(r1)מה הפלט?
✓ תשובה: ד) 2 / Robot#1:Alpha(p=20) — r1: total=1, id=1, power=10. r2: total=2. r1.upgrade(5)→power=15, מחזיר r1. .upgrade(5)→power=20, מחזיר r1. Robot.total=2. r1.__str__="Robot#1:Alpha(p=20)".
גאמא סגנון ב׳
class Box: def __init__(self, w, h, d): self._w, self._h, self._d = w, h, d @property def volume(self): return self._w * self._h * self._d def __eq__(self, other): return self.volume == other.volume def __lt__(self, other): return self.volume < other.volume b1 = Box(2, 3, 4) b2 = Box(4, 3, 2) b3 = Box(1, 1, 1) print(b1 == b2) print(b3 < b1)מה הפלט?
✓ תשובה: ד) True / True — b1.volume=2×3×4=24. b2.volume=4×3×2=24. __eq__ משווה volumes: 24==24 → True. b3.volume=1×1×1=1. __lt__: 1 < 24 → True.
גאמא סגנון ג׳
מה יקרה אם לא מגדירים __eq__ — איך Python תשווה שני objects?
✓ תשובה: ג) — ללא __eq__, Python משתמשת ב-identity (id()). שני objects שונים → id() שונה → False, גם אם כל הנתונים זהים. לכן חשוב להגדיר __eq__ כשרוצים שוויון לפי ערכים.
שאלות תרגול ותשובות (5)

שאלה 1 · מה ההבדל בין Class לבין Object?

  • א. Class הוא התבנית, Object הוא מופע קונקרטי שנוצר ממנה
  • ב. Object הוא התבנית, Class הוא המופע
  • ג. שניהם אותו דבר בפייתון
  • ד. Class קיים רק ב-C# ולא בפייתון
תשובה: א

Class = שרטוט (תבנית); Object/instance = הבית שנבנה לפי השרטוט ומחזיק ערכים משלו.

שאלה 2 · מה יודפס?

class Counter:
    count = 0
    def __init__(self):
        Counter.count += 1

c1 = Counter()
c2 = Counter()
print(Counter.count)
  • א. 0
  • ב. 1
  • ג. 2
  • ד. שגיאה
תשובה: ג

count הוא class attribute משותף, וכל קריאה ל-__init__ מקדמת אותו — אחרי שני מופעים הערך 2.

שאלה 3 · מה יודפס?

class A:
    x = []

a = A()
b = A()
a.x.append(1)
print(b.x)
  • א. []
  • ב. [1]
  • ג. None
  • ד. שגיאה
תשובה: ב

הרשימה היא class attribute אחת המשותפת לכל המופעים — שינוי דרך a נראה גם ב-b.

שאלה 4 · מהו הבנאי האמיתי בפייתון, זה שיוצר את האובייקט בזיכרון?

  • א. __init__
  • ב. __new__
  • ג. __call__
  • ד. __create__
תשובה: ב

__new__ יוצר ומחזיר את האובייקט, ורק אחריו __init__ מאתחל אותו ומחזיר None.

שאלה 5 · מהי המשמעות של private ב-C# מבחינת אבטחה?

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

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