GammaCyber

OOP חלק ב׳ — Inheritance ו-Design Patterns

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

// סקירה

OOP חלק ב׳ — Inheritance, Polymorphism ועיצוב מערכת

בשיעור 7 למדת לבנות Class בודד. בשיעור הזה לומדים לבנות ארכיטקטורה — כיצד Classes מדברים אחד עם השני, מורישים ומוחלפים. זה ההבדל בין "אני יודע Python" לבין "אני יודע לעצב תוכנה".

✓נושאי השיעור
  • Inheritance — class Child(Parent), super().__init__(), Override
  • MRO — סדר חיפוש methods, Diamond Problem, C3 linearization
  • Abstract Classes — ABC, @abstractmethod, חובת מימוש
  • Polymorphism — אותו interface, מימושים שונים, Duck Typing
  • Composition vs Inheritance — IS-A vs HAS-A, "Favor Composition"
  • Singleton Pattern — instance אחד בלבד, __new__
  • Factory Pattern — הסתרת יצירה, Open/Closed Principle
  • Observer Pattern — Subject/Observer, subscribe/notify
  • עיצוב מערכת — שיטת 5 צעדים כמו בסדנה
🎯 קשר למיון גאמא
סדנה: "בנה מערכת ניהול חיות מחמד — Animal, Dog, Cat, Fish — עם inheritance ו-polymorphism". ראיון: "הסבר Singleton. מה Factory Pattern? מה ההבדל בין Inheritance ל-Composition?" שאלון: "מה ABC? מה abstract method? מה Duck Typing?"
// פרק 1

Inheritance: ירושה

Inheritance מאפשרת ל-Class לרשת attributes ו-methods מ-Class אחר. כלל ה-IS-A: Dog IS-A Animal → ירושה הגיונית. Dog IS-A Vehicle → לא הגיוני!

class Animal: def __init__(self, name: str, age: int): self.name = name self.age = age self._energy = 100 def eat(self, food: str): self._energy += 10 print(f"{self.name} אוכל {food}") def describe(self) -> str: return f"{self.name} (גיל {self.age})" def __str__(self): return self.describe() class Dog(Animal): # ← Dog יורש מ-Animal def __init__(self, name: str, age: int, breed: str): super().__init__(name, age) # ← חובה! קורא ל-__init__ של Animal self.breed = breed def bark(self): print(f"{self.name}: וואוו!") def describe(self) -> str: # ← Override! base = super().describe() # ← קורא לגרסה של Parent return f"{base}, גזע: {self.breed}" class Cat(Animal): def __init__(self, name, age, indoor=True): super().__init__(name, age) self.indoor = indoor def meow(self): print(f"{self.name}: מיאו!") def describe(self) -> str: base = super().describe() return f"{base}, {'פנימי' if self.indoor else 'חיצוני'}" rex = Dog("Rex", 3, "Golden Retriever") luna = Cat("Luna", 2, indoor=True) print(rex) # Rex (גיל 3), גזע: Golden Retriever rex.eat("עצם") # Rex אוכל עצם ← מ-Animal! rex.bark() # Rex: וואוו! print(isinstance(rex, Dog)) # True print(isinstance(rex, Animal)) # True ← ירושה! print(issubclass(Dog, Animal)) # True

super() — גישה ל-Parent

class Vehicle: def __init__(self, brand, speed): self.brand, self.speed = brand, speed class Car(Vehicle): def __init__(self, brand, speed, doors): super().__init__(brand, speed) # חובה! self.doors = doors class ElectricCar(Car): def __init__(self, brand, speed, doors, battery): super().__init__(brand, speed, doors) self.battery = battery tesla = ElectricCar("Tesla", 250, 4, 100) print(tesla.brand, tesla.speed, tesla.doors, tesla.battery) # Tesla 250 4 100
הרחבה C# — Inheritance ו-override ›
public class Animal { public string Name { get; } public int Age { get; } public Animal(string name, int age) { Name=name; Age=age; } public virtual string Describe() // virtual = ניתן ל-override => $"{Name} (גיל {Age})"; } public class Dog : Animal // : Animal = ירושה { public string Breed { get; } public Dog(string name, int age, string breed) : base(name, age) // base(...) = super().__init__(...) { Breed = breed; } public override string Describe() // override = מחליף Virtual => $"{base.Describe()}, גזע: {Breed}"; }
✏תרגול עצמי3 שאלות
שאלה 1.1
מה super().__init__() עושה? מה יקרה אם לא נקרא לו?
✓ תשובה: א) — super().__init__() קורא ל-__init__ של Parent Class. בלי זה, self.name ו-self.age לא יאותחלו → AttributeError בגישה אליהם.
שאלה 1.2
class A: def greet(self): return "A" class B(A): def greet(self): return "B" class C(A): def greet(self): return "C" class D(B, C): pass print(D().greet())מה יחזיר? (MRO)
✓ תשובה: ב) B — MRO של D: D → B → C → A → object. Python מחפשת greet() בסדר זה. מוצאת ב-B ראשונה → מחזיר "B".
שאלה 1.3
מה ישוב isinstance(rex, Animal) כשרex הוא Dog(Animal)?
✓ תשובה: ג) True — isinstance() בודק את שרשרת הירושה. כי Dog יורש מ-Animal, כל Dog הוא גם Animal. isinstance(rex, Animal) = True.
🎯 דגש למבחן
Diamond Problem — כלל אצבע פרקטי ל-MRO. כש-class D(B, C) ושתיהן יורשות מ-A, פייתון פותרת את הכפילות עם MRO (Method Resolution Order) לפי אלגוריתם C3. כלל האצבע לבחינה: קודם המחלקה עצמה, אחר כך ההורים משמאל לימין, ומחלקת-אב משותפת נבדקת רק אחרי כל מי שיורש ממנה — כלומר D → B → C → A → object. אפשר לוודא תמיד עם D.__mro__. שתי מסקנות: super() לא בהכרח מפנה להורה הראשון אלא לבא בתור ב-MRO, ולכן בירושה מרובה כל מחלקה חייבת לקרוא ל-super().__init__() כדי שהשרשרת לא תישבר.
// פרק 3

Abstract Classes: חוזה שחובה לממש

Abstract Class הוא class שלא ניתן ליצור ממנו instances ישירות. הוא מגדיר Abstract Methods — מתודות שכל תת-מחלקה חייבת לממש. זהו "חוזה" בין ה-Base לבין Children.

from abc import ABC, abstractmethod import math class Shape(ABC): # ABC = Abstract Base Class def __init__(self, color: str = "שחור"): self.color = color @abstractmethod def area(self) -> float: """חישוב שטח — חייב להיות מממש ב-subclass""" pass @abstractmethod def perimeter(self) -> float: pass # Method רגילה — נורש לכולם, לא חובה לoverride def describe(self) -> str: return (f"{type(self).__name__} {self.color}: " f"שטח={self.area():.2f}, היקף={self.perimeter():.2f}") def __str__(self): return self.describe() class Circle(Shape): def __init__(self, radius, color="שחור"): super().__init__(color) self.radius = radius def area(self): return math.pi * self.radius ** 2 def perimeter(self): return 2 * math.pi * self.radius class Rectangle(Shape): def __init__(self, width, height, color="שחור"): super().__init__(color) self.width, self.height = width, height def area(self): return self.width * self.height def perimeter(self): return 2 * (self.width + self.height) shapes = [Circle(5, "אדום"), Rectangle(4, 6, "כחול")] for shape in shapes: print(shape) # s = Shape() # TypeError! Cannot instantiate abstract class
! שכחת לממש abstract method ›
class BadShape(Shape): def area(self): return 42 # שכחנו perimeter()! b = BadShape() # TypeError: Can't instantiate abstract class BadShape # with abstract method perimeter # אם אפילו method אחת abstract לא מומשה — TypeError!
הרחבה C# — Abstract Classes ›
public abstract class Shape // abstract = לא ניתן ל-instantiate { public string Color { get; } public Shape(string color) { Color = color; } public abstract double Area(); // abstract = חובה לממש public abstract double Perimeter(); // abstract = חובה לממש public string Describe() // לא abstract — נורש => $"{GetType().Name}: Area={Area():F2}"; } public class Circle : Shape { public double Radius { get; } public Circle(double r, string color="שחור") : base(color) { Radius=r; } public override double Area() => Math.PI * Radius * Radius; public override double Perimeter() => 2 * Math.PI * Radius; }
✏תרגול עצמי3 שאלות
שאלה 3.1
מה יקרה? from abc import ABC, abstractmethod; class Shape(ABC): @abstractmethod def area(self): pass; s = Shape()
✓ תשובה: ב) TypeError — לא ניתן ליצור instance מ-Abstract Class ישירות. Python זורקת TypeError ברגע היצירה, עוד לפני קריאה לאיזשהו method.
שאלה 3.2
מה ההבדל בין @abstractmethod לבין method רגילה ב-ABC?
✓ תשובה: ד) — @abstractmethod מגדיר "חוזה". אם subclass לא מממש — TypeError ביצירה. Method רגילה ב-ABC נורשת ועובדת מיד, override הוא אופציונלי.
שאלה 3.3
מה מייצג "Polymorphism" בהקשר של ABC?
✓ תשובה: ג) — Polymorphism: for shape in shapes: print(shape.area()) — Python קוראת לנכון בכל object. קוד זהה, מימושים שונים. ABC מבטיח שלכולם יש area().
// פרק 5

Composition vs Inheritance

Composition = "HAS-A". במקום לרשת, מחזיקים אובייקט כ-attribute. הכלל: "Favor Composition over Inheritance".

קריטריוןInheritanceComposition
קשרIS-A (Dog IS-A Animal)HAS-A (Car HAS-A Engine)
גמישותנמוכה — חוזה קשיחגבוהה — ניתן להחליף
שימוששיתוף התנהגות משותפתבנייה מרכיבים
בעיהDiamond Problem, tight couplingיותר קוד boilerplate
# BAD: Car IS-A Engine? לא! class BadCar(Engine): # ← לא הגיוני! pass # GOOD: Car HAS-A Engine (Composition) class Engine: def __init__(self, hp): self.hp = hp self._running = False def start(self): self._running = True print(f"מנוע {self.hp} כ.ס. פועל") @property def is_running(self): return self._running class GPS: def navigate(self, dest): print(f"מנווט ל-{dest}") class Car: def __init__(self, brand, hp): self.brand = brand self._engine = Engine(hp) # HAS-A Engine self._gps = GPS() # HAS-A GPS def start(self): self._engine.start() # delegation! print(f"{self.brand} מוכן לנסיעה") def navigate(self, dest): if not self._engine.is_running: print("אי אפשר לנווט — מנוע כבוי!") return self._gps.navigate(dest) # delegation! car = Car("Toyota", 150) car.start() # מנוע 150 כ.ס. פועל → Toyota מוכן לנסיעה car.navigate("ירושלים") # מנווט לירושלים
✏תרגול עצמי3 שאלות
שאלה 5.1
מה עדיף: class Car(Engine) או class Car: def __init__(self): self.engine = Engine()?
✓ תשובה: ג) Composition — Car IS-NOT-A Engine! ירושה לא הגיונית. Composition נכון: Car HAS-A Engine. יתרון: ניתן להחליף Engine runtime, Encapsulation נכון.
שאלה 5.2
מה "Duck Typing" ב-Python?
✓ תשובה: ג) — Duck Typing: אם לאובייקט יש את המתודות הנדרשות — מספיק, ללא קשר לtype. גמישות מקסימלית. אם obj.speak() לא קיים — AttributeError רק בריצה.
שאלה 5.3
מה ההבדל בין Overriding לבין Overloading? מה Python תומכת?
✓ תשובה: ד) — Override: Dog.speak() מחליף Animal.speak(). Python תומכת מלא. Overload: void Foo(int x) ו-void Foo(string s) — ב-C# יש, Python לא. פתרון ב-Python: ברירות מחדל או *args.
🎯 דגש למבחן
החיסרון של Duck Typing — שגיאות שמתגלות רק בזמן ריצה. "If it walks like a duck..." — פייתון לא בודקת טיפוסים בקומפילציה אלא רק מנסה לקרוא למתודה. היתרון הוא גמישות; החיסרון הוא ששגיאת טיפוס מתפוצצת ב-Production ולא בבנייה: אובייקט שחסרה בו speak() יעבור בשקט בכל הבדיקות הסטטיות ויזרוק AttributeError רק כשהזרימה תגיע אליו. ב-C#, לעומת זאת, הקומפיילר פוסל את הקוד מראש. הריפוי המקצועי: ABC + @abstractmethod, Type Hints עם mypy, ו-Protocol מ-typing — Duck Typing עם בדיקה סטטית.
// פרקים 6–8

Design Patterns: Singleton, Factory, Observer

Design Patterns הן פתרונות מוכחים לבעיות עיצוב נפוצות. בראיון גאמא — שאלות עליהם קלאסיות.

🏗️ Singleton Pattern

🎯 Singleton
מטרה: ודא שרק instance אחד של class קיים. מתי: Database connection, Logger, Config manager. מנגנון: __new__ נקראת לפני __init__ — בודקת אם instance קיים.
class Singleton: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance def __init__(self): if hasattr(self, '_initialized'): return self._initialized = True self.config = {} s1 = Singleton() s2 = Singleton() print(s1 is s2) # True! — אותו object s1.config["key"] = "value" print(s2.config) # {'key': 'value'} ← אותו object!

🏗️ Factory Pattern

🎯 Factory
מטרה: הסתר את לוגיקת יצירת Objects. מתי: כשסוג ה-Object נקבע בריצה. יתרון: Open/Closed Principle — אפשר להוסיף סוגים חדשים ללא שינוי קוד קיים.
from abc import ABC, abstractmethod class Notification(ABC): @abstractmethod def send(self, message, recipient): pass class EmailNotification(Notification): def send(self, m, r): print(f"[EMAIL] → {r}: {m}") class SMSNotification(Notification): def send(self, m, r): print(f"[SMS] → {r}: {m}") class NotificationFactory: _creators = { "email": EmailNotification, "sms": SMSNotification, } @classmethod def create(cls, notification_type: str) -> Notification: creator = cls._creators.get(notification_type.lower()) if creator is None: raise ValueError(f"סוג לא מוכר: {notification_type}") return creator() @classmethod def register(cls, name, creator): cls._creators[name] = creator # Open/Closed! # שימוש: NotificationFactory.create("email").send("שלום!", "alice@ex.com") NotificationFactory.create("sms").send("קוד: 1234", "+972-50")

🏗️ Observer Pattern

🎯 Observer
מטרה: כשאובייקט משתנה — כל המנויים מקבלים הודעה. מתי: Event systems, UI updates, Real-time notifications. שמות נוספים: Publish/Subscribe, Event/Listener.
from abc import ABC, abstractmethod class Observer(ABC): @abstractmethod def update(self, event_type: str, data): pass class Subject: def __init__(self): self._observers = [] def subscribe(self, observer): self._observers.append(observer) def unsubscribe(self, observer): self._observers.remove(observer) def notify(self, event_type, data=None): for observer in self._observers: observer.update(event_type, data) class SecuritySystem(Subject): def __init__(self): super().__init__() self._armed = False def arm(self): self._armed = True self.notify("armed", {"time": "09:00"}) def trigger_alert(self, location): if self._armed: self.notify("alert", {"location": location}) class EmailAlert(Observer): def __init__(self, email): self.email = email def update(self, event_type, data): if event_type == "alert": print(f"[EMAIL→{self.email}] אזעקה: {data['location']}") system = SecuritySystem() system.subscribe(EmailAlert("owner@home.com")) system.arm() system.trigger_alert("חלון כניסה")
✏תרגול עצמי3 שאלות
שאלה 6.1
מה Singleton מבטיח?
✓ תשובה: ד) — Singleton מבטיח instance אחד בלבד. __new__ בודקת _instance לפני יצירה. s1 = Singleton(); s2 = Singleton(); s1 is s2 → True.
שאלה 6.2
מה Factory Pattern פותר?
✓ תשובה: ג) — Factory מסתיר את לוגיקת יצירת objects. במקום if/elif ארוך — Factory.create("email"). Open/Closed: register() מוסיף סוג חדש ללא שינוי קוד קיים.
שאלה 6.3
מה ההבדל בין Subject לבין Observer?
✓ תשובה: ג) — Subject (SecuritySystem) שומר רשימת observers ומפעיל notify() כשמשהו קורה. Observer (EmailAlert) מממש update() ומגיב לאירועים. Decoupling — Subject לא יודע מי מקשיב.
🎯 דגש למבחן
Singleton הוא לעיתים קרובות Anti-Pattern — ו-Observer הוא ה-Webhook שאתם מכירים. Singleton: הוא מצב גלובלי בתחפושת — יוצר צימוד סמוי בין מודולים, מקשה על בדיקות (אי אפשר להחליף ב-mock, ומצב דולף בין טסטים) ומסוכן ב-multi-threading אם היצירה אינה נעולה. לכן בקוד מודרני מעדיפים Dependency Injection: מעבירים את המופע היחיד כפרמטר במקום לקבע אותו גלובלית. Observer: זה בדיוק המנגנון שמאחורי Webhooks — ה-Subject הוא השרת המרוחק, ה-Observers הם ה-URLs הרשומים, ו-notify() הוא בקשת ה-POST שנשלחת בכל אירוע. אותו רעיון בדיוק גם ב-Event Listeners בדפדפן וב-Pub/Sub בתורי הודעות.
// פרק 10

עיצוב מערכת מלאה: כמו בסדנה

בסדנה תקבל משימה: "בנה מערכת X". הנה שיטת 5 צעדים לגשת אליה.

🗺️ שיטת 5 הצעדים לעיצוב מערכת OOP
1. זיהוי Entities: מה "הדברים" במערכת? → Classes
2. זיהוי קשרים: IS-A? HAS-A? מי יורש ממי?
3. Abstract Classes: מה משותף לכולם? → ABC
4. Polymorphism: איפה ה-behavior משתנה בין סוגים?
5. Design Patterns: יש Singleton? Factory? Observer?
from abc import ABC, abstractmethod # דוגמה: מערכת HR — Employee, Manager, CEO class Employee(ABC): def __init__(self, name: str, base_salary: float): self.name = name self._base = base_salary @abstractmethod def calculate_salary(self) -> float: pass @property def salary(self) -> float: return self.calculate_salary() def __str__(self): return f"{type(self).__name__}: {self.name} — ₪{self.salary:,.0f}" class RegularEmployee(Employee): def calculate_salary(self): return self._base class Manager(Employee): def __init__(self, name, base, bonus_pct=0.2): super().__init__(name, base) self._bonus_pct = bonus_pct self._team = [] def add_to_team(self, emp): self._team.append(emp) def calculate_salary(self): return self._base * (1 + self._bonus_pct) class CEO(Manager): def __init__(self, name, base, bonus_pct=0.5, equity=0): super().__init__(name, base, bonus_pct) self._equity = equity def calculate_salary(self): return super().calculate_salary() + self._equity class Department: def __init__(self, name): self.name = name; self._employees = [] def hire(self, emp): self._employees.append(emp) def total_payroll(self): return sum(e.salary for e in self._employees) def __str__(self): return f"מחלקת {self.name}, שכר כולל: ₪{self.total_payroll():,.0f}" dev = Department("פיתוח") dev.hire(RegularEmployee("Alice", 15000)) dev.hire(Manager("Carol", 20000, 0.25)) dev.hire(CEO("Dan", 50000, 0.5, 10000)) for emp in dev._employees: # Polymorphism! print(emp) print(dev)
✏תרגול סיום — גאמא סגנון3 שאלות
גאמא סגנון א׳
class Parent: def hello(self): print("Parent") self.world() # ← Template Method! def world(self): print("Parent.world") class Child(Parent): def world(self): print("Child.world") Child().hello()מה הפלט?
✓ תשובה: א) Parent → Child.world — Child().hello() → hello() מ-Parent (לא overriden) → מדפיס "Parent" → קורא self.world(). self הוא Child instance! → Child.world() → "Child.world". זהו Template Method Pattern!
גאמא סגנון ב׳
class S: _i = None def __new__(cls): if cls._i is None: cls._i = super().__new__(cls) return cls._i a = S(); b = S(); c = S() print(a is b, b is c)מה הפלט?
✓ תשובה: ג) True True — Singleton! __new__ נקראת 3 פעמים, אבל רק בפעם הראשונה יוצר instance. שאר הפעמים מחזיר cls._i — אותו object. a, b, c כולם אותו instance.
גאמא סגנון ג׳
מה MRO של class D(B, C) כשB(A) ו-C(A)?
✓ תשובה: א) D → B → C → A → object — C3 Linearization: D קודם, אחר כך left-to-right לפי הצהרת הירושה (B, C), ורק אחרי כולם — A (שנמצא ב-both B ו-C) ואז object.
// אתגר מעשי

אתגר מעשי — OOP: ירושה ופולימורפיזם

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

T2 · בסיסי200 נק'~10 דק'debug
תיקון באג — ירושה בלי super()
תת-המחלקה Manager יורשת מ-Employee אך שוכחת לאתחל את מחלקת-הבסיס, ולכן name ו-salary לא קיימים ו-summary() קורס ב-AttributeError. מצא ותקן כך ש-Manager('Dana',100,5).summary() יחזיר 'Dana: 100, team 5'.
// הקוד השבור לתיקון
class Employee: def __init__(self, name, salary): self.name = name; self.salary = salary class Manager(Employee): def __init__(self, name, salary, team): self.team = team # באג כאן def summary(self): return f"{self.name}: {self.salary}, team {self.team}"
דוגמאות
Manager('Dana', 100, 5).summary() → 'Dana: 100, team 5'
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס תיקון באג — ירושה בלי super() ›
class Employee: def __init__(self, name, salary): self.name = name; self.salary = salary class Manager(Employee): def __init__(self, name, salary, team): super().__init__(name, salary) # התיקון: אתחול מחלקת-הבסיס self.team = team def summary(self): return f"{self.name}: {self.salary}, team {self.team}"
T3 · בינוני350 נק'~25 דק'algorithmic
היררכיית Shape — פולימורפיזם
בנה מחלקה אבסטרקטית Shape עם מתודה אבסטרקטית area() (אי אפשר ליצור ממנה מופע ישירות). ממש שלוש תת-מחלקות: Rectangle(w,h), Circle(r) (שטח πr²), Triangle(b,h) (שטח b·h/2). לבסוף ממש פונקציה total_area(shapes) שמחזירה את סכום השטחים של רשימת צורות — פולימורפית, בלי לבדוק טיפוסים.
דוגמאות
Rectangle(3,4).area() → 12
Circle(2).area() → 12.566... (=4π)
total_area([Rectangle(2,2), Triangle(3,4)]) → 10.0
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס היררכיית Shape — פולימורפיזם ›
from abc import ABC, abstractmethod import math class Shape(ABC): @abstractmethod def area(self): ... class Rectangle(Shape): def __init__(self, w, h): self.w = w; self.h = h def area(self): return self.w * self.h class Circle(Shape): def __init__(self, r): self.r = r def area(self): return math.pi * self.r * self.r class Triangle(Shape): def __init__(self, b, h): self.b = b; self.h = h def area(self): return self.b * self.h / 2 def total_area(shapes): return sum(s.area() for s in shapes) # פולימורפיזם
// סיכום

סיכום השיעור

✓מה למדנו בשיעור זה
  • Inheritance: class Child(Parent), super().__init__(), Override
  • MRO: סדר חיפוש methods, Diamond Problem, C3 linearization
  • Abstract Classes: ABC, @abstractmethod, חובת מימוש ב-subclass
  • Polymorphism: אותו interface, מימושים שונים, Duck Typing
  • Composition vs Inheritance: IS-A vs HAS-A, "Favor Composition"
  • Singleton: instance אחד בלבד, __new__, DB/Config/Logger
  • Factory: הסתרת יצירה, dict של creators, Open/Closed Principle
  • Observer: Subject/Observer, subscribe/unsubscribe/notify
  • עיצוב מערכת: שיטת 5 צעדים — Entities, Relations, ABC, Poly, Patterns
🎯 השיעור הבא
שיעור 9 — אלגוריתמיקה ומבני נתונים
לפני שיעור 9 — כתוב מערכת Employee מהראש ב-15 דקות. אם לא — חזור על שיעורים 7–8.
שאלות תרגול ותשובות (10)

שאלה 1 · מה עושה super().__init__() במחלקה יורשת?

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

בלי הקריאה, האתחול של האב לא מתבצע והשדות שלו לא קיימים במופע.

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

class A:
    def greet(self): return "A"

class B(A):
    def greet(self): return "B"

print(B().greet())
  • א. A
  • ב. B
  • ג. AB
  • ד. שגיאה
תשובה: ב

Overriding — המימוש של B דורס את זה של A.

שאלה 3 · מה יקרה בעת יצירת מופע של מחלקה יורשת שלא מימשה מתודה שסומנה ב-@abstractmethod?

  • א. המתודה תתעלם
  • ב. ייזרק TypeError בזמן היצירה
  • ג. המתודה תרוץ מהאב
  • ד. הקוד יתקמפל אך יחזיר None
תשובה: ב

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

שאלה 4 · מהו סדר ה-MRO של class D(B, C) כאשר B(A) ו-C(A)?

  • א. D → B → C → A → object
  • ב. D → A → B → C
  • ג. D → C → B → A
  • ד. אין סדר מוגדר — נזרקת שגיאה
תשובה: א

C3 Linearization: המחלקה, ההורים משמאל לימין, ורק אז האב המשותף.

שאלה 5 · מהו החיסרון המרכזי של Duck Typing?

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

אין בדיקה סטטית — חוסר מתודה יתגלה כ-AttributeError רק כשהקוד ירוץ.

שאלה 6 · מתי נעדיף Composition על פני Inheritance?

  • א. כשהיחס הוא "יש-לו" (HAS-A) ולא "הוא-סוג-של" (IS-A)
  • ב. תמיד, ירושה אסורה
  • ג. כשיש רק מחלקה אחת
  • ד. כשעובדים בפייתון בלבד
תשובה: א

למכונית יש מנוע — היא אינה מנוע; לכן הרכבה ולא ירושה.

שאלה 7 · מדוע Singleton נחשב לעיתים Anti-Pattern?

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

מצב גלובלי דולף בין טסטים ומונע החלפה ב-mock; החלופה המודרנית היא Dependency Injection.

שאלה 8 · איזה מנגנון בעולם האמיתי מממש בפועל את דפוס Observer?

  • א. Garbage Collection
  • ב. Webhooks
  • ג. Name Mangling
  • ד. Type Casting
תשובה: ב

ה-Subject שולח POST לכל ה-URLs הרשומים — בדיוק notify() לכל ה-Observers.

שאלה 9 · מה מבטיח עקרון ההחלפה של ליסקוב (Liskov Substitution)?

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

היורשת חייבת לכבד את החוזה של האב — אחרת הפולימורפיזם נשבר.

שאלה 10 · מה מבדיל @abstractmethod ממתודה רגילה בתוך ABC?

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

המתודה האבסטרקטית מגדירה חוזה מחייב; מתודה רגילה ב-ABC היא מימוש ברירת מחדל שניתן לרשת כמות שהוא.