GammaCyber

התנעת פרויקט הדגל — Git, PRD, MVP

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

// סקירה

התנעת פרויקט הדגל

שיעור זה שונה מכל שיעור שלמדת. אין Dry Run. אין MCQ. יש פעולה אחת: לבחור פרויקט, לתכנן אותו כמו מהנדס, ולהתחיל לבנות אותו.

✓נושאי השיעור
  • בחירת פרויקט — קריטריונים, רשימת רעיונות, מה לא לבחור
  • PRD — מסמך דרישות: feature list, out of scope, success criteria
  • MVP — Minimum Viable Product, "Done is better than perfect"
  • Git — init, add, commit, push, branch, merge, .gitignore
  • GitHub — Remote repository, README.md, Git history כ-portfolio
  • venv — בידוד סביבת Python, requirements.txt
  • מבנה פרויקט מקצועי — src/, tests/, main.py, __init__.py
  • Agile Milestones — פירוק לאבני דרך ניתנות לבדיקה
  • unittest — Tests directory, setUp, assertTrue, assertLess
  • Password Analyzer — דוגמת פרויקט מלאה: Singleton, OOP, CLI
🎯 קשר למיון גאמא
ראיון: "ספר לי על פרויקט שבנית" — כמעט כל מועמד שעבר שמע שאלה זו. שאלון: שאלות על Git, GitHub, מבנה פרויקט. סדנה: מי שתרגל בניית Classes בפרויקט אמיתי — יצלח את הסדנה הרבה יותר בקלות.
// פרק 1

בחירת הפרויקט: הקריטריונים שחשובים

הטעות הנפוצה: לבחור "פרויקט מרשים" בלי לחשוב כיצד לדבר עליו. פרויקט פשוט שאתה מבין לעומק ויכול להסביר עדיף על פרויקט מסובך שמשכת מ-GitHub.

קריטריוןמה בודקיםמשקל
רלוונטיות לסייבר/טכנולוגיהחיבור לנושא המיון — כלי אבטחה, ניתוח נתונים, אוטומציה⭐⭐⭐
שימוש ב-OOPClasses, Inheritance, Encapsulation — ניראה בסדנה⭐⭐⭐
קצר ומושלם > ארוך ושבורMVP שעובד 100% עדיף על פרויקט ענק שמתרסק⭐⭐⭐
Git History נקי15+ commits עם הודעות ברורות — מוכיח תהליך עבודה⭐⭐
README מפורטמה הפרויקט, כיצד להריץ, דוגמאות פלט⭐⭐

רעיונות מומלצים לפרויקט

🔐 Password Strength Analyzer — מנתח חוזק סיסמה עם class PasswordPolicy ו-Singleton.
🌐 Port Scanner בסיסי — סורק פורטים פתוחים ב-IP נתון עם socket.
📊 Log Analyzer — קורא קובץ log, מנתח שגיאות, מדפיס סטטיסטיקה (File I/O).
🔑 Caesar/Vigenere Cipher — הצפנה קלאסית עם ממשק encrypt/decrypt. OOP מלא.
🗂️ Student Grade Manager — מנהל ציונים, ממוצעים, תעודות. Class system עם Inheritance.

! אל תבחר את הפרויקטים האלה ›

❌ "פרויקט AI" שמשתמש ב-API של ChatGPT — זה לא שלך. אי אפשר להסביר את הקוד.
❌ פרויקט שהורדת מ-GitHub ו"שיפרת" — הראיין יבין מיד.
❌ פרויקט Web מלא עם React+Backend — יותר מדי. אתה צריך עומק, לא רוחב.
❌ פרויקט "מרשים" שאתה לא מבין: "בניתי מנוע machine learning" — "ספר לי על הארכיטקטורה" — "..."

✏תרגול עצמי3 שאלות
שאלה 1.1
שני מועמדים: אחד עם Password Analyzer ב-200 שורות קוד נקי + OOP + Git עם 20 commits. השני עם Web App עם 500 שורות שעובד בקושי. מי יותר מרשים בראיון?
✓ תשובה: ד) — הראיין בגאמא מחפש: הבנה עמוקה, תהליך עבודה מסודר, יכולת הסבר. "Less is more" הוא עיקרון מהנדסי אמיתי. python main.py → crash = רושם גרוע. python main.py → עובד = מרשים.
שאלה 1.2
מה MVP (Minimum Viable Product)?
✓ תשובה: ג) — MVP = "Done is better than perfect". עובד end-to-end, ניתן לבדיקה, ניתן להרצה ב-3 פקודות מ-README, קוד קריא ומתועד. Feature שבור = חוב טכני.
שאלה 1.3
מה Out of Scope ב-PRD?
✓ תשובה: ד) — Out of Scope הוא מכוון, לא שגגה. "הוחלט לא לממש כרגע" = scope management. בדוגמת Password Analyzer: GUI, חיבור ל-DB, HIBP API — כולם Out of Scope ב-V1.
// פרק 4

Git: Version Control מהאפס

בלי Git = אין ראיות שעבדת. עם Git = רואים 25 commits → רואים תהליך עבודה אמיתי. GitHub Profile = ה-LinkedIn של מהנדס תוכנה.

Workflow יומי: Add → Commit → Push

# הגדרה ראשונה (פעם אחת בלבד) git config --global user.name "שמך" git config --global user.email "email@example.com" # יצירת Repository mkdir my_project && cd my_project git init # Workflow יומי git status # ראה מה השתנה (אדום=unstaged, ירוק=staged) git add . # Stage כל הקבצים git commit -m "Add password length validation" # Snapshot git push origin main # שלח ל-GitHub
! הודעות Commit — הטעות הנפוצה ביותר ›

❌ "fix" — לא מסביר כלום
❌ "update" — update מה?
❌ "asdfgh" — נראה מגוחך לראיין
✅ "Add input validation for password length"
✅ "Fix: handle empty password input correctly"
✅ "Refactor: extract scoring logic to separate method"

כלל: הודעת commit = משפט שמסביר מה ולמה. Past tense, English (convention).

Branches ו-.gitignore

# Branch — עבודה מקבילה git checkout -b feature/blacklist-check # צור + עבור git add . && git commit -m "Add common password blacklist" git checkout main git merge feature/blacklist-check # פקודות שימושיות git log --oneline # history קצר git diff # מה השתנה (לפני stage) git stash # שמור שינויים "בכיס" git stash pop # הוצא מהכיס # .gitignore — מה לא להעלות # __pycache__/ *.pyc # venv/ .venv/ # .env secrets.json ← אל תעלה אף פעם! # .idea/ .vscode/
הרחבה README.md ומבנה פרויקט מקצועי ›
# מבנה פרויקט מומלץ: password-analyzer/ ├── main.py # Entry point ├── README.md # תיעוד ├── requirements.txt # ספריות נדרשות ├── .gitignore ├── src/ │ ├── __init__.py │ ├── analyzer.py # class PasswordAnalyzer │ ├── policy.py # class PasswordPolicy (Singleton) │ └── report.py # class PasswordReport ├── tests/ │ ├── __init__.py │ └── test_analyzer.py └── data/ └── common_passwords.txt
# main.py — if __name__ == "__main__" import argparse from src import PasswordAnalyzer def main(): parser = argparse.ArgumentParser(description="Password Strength Analyzer") parser.add_argument("password", help="Password to analyze") args = parser.parse_args() analyzer = PasswordAnalyzer() report = analyzer.analyze(args.password) report.display() if __name__ == "__main__": main() # __name__ == "__main__" רק כשמריצים ישירות! # כשמייבאים כmodule → main() לא נקרא → Testable!
✏תרגול עצמי3 שאלות
שאלה 4.1
מה ההבדל בין git add לבין git commit?
✓ תשובה: ג) — Workflow: עבוד → git add . (stage) → git commit -m "message" (snapshot) → git push (GitHub). שלושה שלבים נפרדים.
שאלה 4.2
מה .gitignore? ציין 3 סוגי קבצים שתמיד צריך להוסיף.
✓ תשובה: ב) — .gitignore מגדיר מה Git מתעלם ממנו. חיוני: venv/ (גדול ולא portable), __pycache__/ (compiled files), .env (אל תעלה סיסמאות ו-API Keys לGitHub אף פעם!).
שאלה 4.3
מה if __name__ == "__main__": אומר? למה חשוב?
✓ תשובה: ג) — __name__ == "__main__" רק בהרצה ישירה (python main.py). כשמיובא: __name__ = שם המודול (לא "__main__") → הבלוק לא רץ, ו-main() לא נקרא. חיוני לבדיקות: אפשר לייבא פונקציות מ-main.py בלי שהקוד הראשי ירוץ.
// פרק 6

Virtual Environment: בועה מבודדת לכל פרויקט

venv = סביבת Python מבודדת לכל פרויקט. כל פרויקט יכול להשתמש בגרסאות שונות של ספריות — בלי שיתנגשו.

# יצירה cd my_project python3 -m venv venv # הפעלה source venv/bin/activate # Linux/Mac venv\Scripts\activate # Windows # ה-prompt: (venv) user@machine:~/my_project$ # התקנת ספריות pip install requests pytest # שמירת רשימה pip freeze > requirements.txt # "תעודת הזהות" של הסביבה # כשמישהו מוריד הפרויקט python3 -m venv venv source venv/bin/activate pip install -r requirements.txt # מתקין הכל בבת-אחת # ביטול deactivate
! venv/ לא מועלה ל-GitHub — תמיד ב-.gitignore! ›

תיקיית venv/ יכולה לשקול 50-200MB ומכילה binaries ספציפיים ל-OS. הכל מה שצריך להעלות: requirements.txt (כמה KB). מי שמוריד: יריץ pip install -r requirements.txt וישחזר הכל.

✏תרגול עצמי3 שאלות
שאלה 6.1
מה הפקודה הנכונה ליצירת venv חדש ב-Python 3?
✓ תשובה: א) python3 -m venv venv — הפקודה הסטנדרטית. -m = run as module. venv = שם המודול. venv (השני) = שם תיקיית הסביבה.
שאלה 6.2
מה pip freeze > requirements.txt עושה? למה חשוב?
✓ תשובה: ב) — requirements.txt = "תעודת זהות" הסביבה. כל שורה: package_name==version. חשוב לשיתוף: מי שמוריד הפרויקט יכול לשחזר בדיוק את אותה סביבה.
שאלה 6.3
מה Single Responsibility Principle (SRP)?
✓ תשובה: ד) SRP — מ-SOLID Principles. הפרדת אחריויות מפשטת קריאה, בדיקה, תחזוקה. 4 classes קטנים עם 5 methods > class אחד ענק עם 20 methods.
// פרק 8

Password Analyzer: פרויקט מלא לדוגמה

הנה ארכיטקטורת פרויקט שלמה המדגימה את כל מה שלמדת: Singleton, Encapsulation, Properties, CLI, Tests.

import re from .policy import PasswordPolicy class PasswordAnalyzer: """מנתח חוזק סיסמאות — orchestrates הניתוח.""" def __init__(self): self._policy = PasswordPolicy.get_instance() def analyze(self, password: str): scores, suggestions = {}, [] # בדיקת אורך length = len(password) if length >= 16: scores["length"] = 30 elif length >= 12: scores["length"] = 20 elif length >= 8: scores["length"] = 10 suggestions.append("Increase length to 12+") else: scores["length"] = 0 suggestions.append(f"Too short ({length} chars). Use 12+.") # בדיקת סוגי תווים char_checks = { "uppercase": (r"[A-Z]", 15, "Add uppercase letters"), "lowercase": (r"[a-z]", 15, "Add lowercase letters"), "digits": (r"\d", 15, "Add numbers"), "special": (r"[!@#$%^&*]", 25, "Add special characters"), } for key, (pattern, pts, msg) in char_checks.items(): if re.search(pattern, password): scores[key] = pts else: scores[key] = 0; suggestions.append(msg) # בדיקת סיסמאות נפוצות if self._policy.is_common(password): scores["uniqueness"] = -50 suggestions.insert(0, "⚠️ Common password!") total = max(0, sum(scores.values())) return PasswordReport(password, total, suggestions)
class PasswordPolicy: """Singleton: כלל מדיניות הסיסמאות — instance אחד בלבד.""" _instance = None _common_passwords: set = set() @classmethod def get_instance(cls): if cls._instance is None: cls._instance = cls.__new__(cls) cls._instance._load_common_passwords() return cls._instance def _load_common_passwords(self): try: with open("data/common_passwords.txt") as f: PasswordPolicy._common_passwords = {line.strip().lower() for line in f} except FileNotFoundError: PasswordPolicy._common_passwords = {"password","123456","qwerty","admin"} def is_common(self, password: str) -> bool: return password.lower() in self._common_passwords
הרחבה Tests ו-"ספר לי על פרויקט שבנית" ›
import unittest from src import PasswordAnalyzer class TestPasswordAnalyzer(unittest.TestCase): def setUp(self): self.analyzer = PasswordAnalyzer() def test_short_password_low_score(self): report = self.analyzer.analyze("abc") self.assertLess(report.score, 30) def test_strong_password_high_score(self): report = self.analyzer.analyze("MyP@ssw0rd!123") self.assertGreaterEqual(report.score, 70) def test_empty_password_zero_score(self): report = self.analyzer.analyze("") self.assertEqual(report.score, 0) def test_common_password_penalized(self): report = self.analyzer.analyze("password") self.assertLess(report.score, 20)
✏תרגול סיום — גאמא סגנון3 שאלות
גאמא סגנון א׳ — ראיון
מראיין שואל: "מה ה-Big-O של blacklist check בפרויקט שלך?" — מה התשובה הנכונה ב-Password Analyzer?
✓ תשובה: א) O(1) — set ב-Python = hash table. חיפוש (x in s) = O(1) amortized. לעומת list שחיפוש = O(n). זו בדיוק שאלת ראיון קלאסית — "למה בחרת set ולא list?"
גאמא סגנון ב׳ — Git
מה הפקודה ליצירת branch חדש ומעבר אליו בבת-אחת?
✓ תשובה: א) git checkout -b feature/x — -b = new branch + checkout = עבור אליו. שקול ל: git branch feature/x && git checkout feature/x. בgit החדש: git switch -c feature/x.
גאמא סגנון ג׳ — ארכיטקטורה
מראיין שואל: "למה 3 Classes (Analyzer/Policy/Report) ולא class אחד?" — מה העיקרון?
✓ תשובה: ב) SRP — Single Responsibility Principle מ-SOLID. כל class = אחריות אחת. יתרון: אם Report ישתנה, Analyzer לא נשבר. קל לכתוב test לכל class בנפרד.
// אתגר מעשי

אתגר מעשי — פרויקט הדגל — Git וגרסאות

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

T2 · בסיסי200 נק'~12 דק'algorithmic
השוואת גרסאות סמנטיות (SemVer)
בפרויקט הדגל אתם מתייגים גרסאות. נתונות שתי מחרוזות גרסה a ו-b, כל אחת סדרת מספרים שלמים אי-שליליים מופרדים בנקודה (למשל "1.2.10"). החזר 1 אם a>b, -1 אם a<b, 0 אם שוות. ההשוואה מספרית מקטע-אחר-מקטע; מקטעים חסרים נחשבים 0 (כך ש-"1.2" שווה ל-"1.2.0").
דוגמאות
compare_versions('1.2.10', '1.2.9') → 1
compare_versions('1.2', '1.2.0') → 0
compare_versions('1.0.0', '1.0.1') → -1
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס השוואת גרסאות סמנטיות (SemVer) ›
def compare_versions(a: str, b: str) -> int: pa = [int(x) for x in a.split('.')] pb = [int(x) for x in b.split('.')] n = max(len(pa), len(pb)) pa += [0] * (n - len(pa)); pb += [0] * (n - len(pb)) # חלקים חסרים = 0 return (pa > pb) - (pa < pb)
T3 · בינוני350 נק'~25 דק'algorithmic
התאמת תבנית גלוב (.gitignore-style)
ב-.gitignore משתמשים בתבניות עם תווים כלליים. ממש glob_match(p, s) המחזיר True אם התבנית p תואמת את המחרוזת s במלואה, כאשר '*' תואם רצף כלשהו (כולל ריק) ו-'?' תואם תו יחיד בדיוק; כל תו אחר מילולי. חובה יעילות O(|p|+|s|) (two-pointer): מימוש רקורסיבי תמים שמפצל על כל '*' הוא אקספוננציאלי ויתקע ב-timeout.
דוגמאות
glob_match('*.txt', 'file.txt') → True
glob_match('a?c', 'ac') → False
glob_match('a*c', 'abbbc') → True
הפתרון נשמר מקומית ומשותף עם עמוד „כתיבת קוד”.
פתרון-ייחוס התאמת תבנית גלוב (.gitignore-style) ›
def glob_match(p: str, s: str) -> bool: i = j = 0; star = -1; mark = 0 # two-pointer עם backtrack ל-'*' האחרון while j < len(s): if i < len(p) and (p[i] == s[j] or p[i] == '?'): i += 1; j += 1 elif i < len(p) and p[i] == '*': star = i; mark = j; i += 1 elif star != -1: # התאמה נכשלה — הרחב את ה-'*' האחרון i = star + 1; mark += 1; j = mark else: return False while i < len(p) and p[i] == '*': i += 1 return i == len(p)
// סיכום

סיכום השיעור

✓מה למדנו בשיעור זה
  • בחירת פרויקט: קריטריונים — רלוונטיות, OOP, MVP עובד, Git history, README
  • PRD: feature list, out of scope, success criteria, milestones
  • MVP: קטן ועובד 100% עדיף על גדול ושבור — "Done is better than perfect"
  • Git: init, add, commit, push, branch, merge, .gitignore — workflow יומי
  • GitHub: README.md, Git history כ-portfolio, Activity Graph
  • venv: בידוד סביבת Python, requirements.txt, מה ב-.gitignore
  • מבנה פרויקט: src/, tests/, main.py, __init__.py, data/
  • Agile Milestones: פירוק עבודה לאבני דרך ניתנות לבדיקה
  • unittest: setUp, assertLess/assertGreater — tests כתיעוד חי
🎯 השיעור הבא
שיעור 11 — שפה מומצאת: 5 שלבים, Dry Run, Symbol Table
עד שיעור 11: בחר פרויקט → כתוב PRD → git init → כתוב Class ראשי → git commit + push.
שאלות תרגול ותשובות (12)

שאלה 1.1 · שני מועמדים: אחד עם Password Analyzer ב-200 שורות קוד נקי + OOP + Git עם 20 commits. השני עם Web App עם 500 שורות שעובד בקושי. מי יותר מרשים בראיון?

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

✓ תשובה: ד) — הראיין בגאמא מחפש: הבנה עמוקה, תהליך עבודה מסודר, יכולת הסבר. "Less is more" הוא עיקרון מהנדסי אמיתי. python main.py → crash = רושם גרוע. python main.py → עובד = מרשים.

שאלה 1.2 · מה MVP (Minimum Viable Product)?

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

✓ תשובה: ג) — MVP = "Done is better than perfect". עובד end-to-end, ניתן לבדיקה, ניתן להרצה ב-3 פקודות מ-README, קוד קריא ומתועד. Feature שבור = חוב טכני.

שאלה 1.3 · מה Out of Scope ב-PRD?

# הגדרה ראשונה (פעם אחת בלבד)
git config --global user.name "שמך"
git config --global user.email "email@example.com"

# יצירת Repository
mkdir my_project && cd my_project
git init

# Workflow יומי
git status          # ראה מה השתנה (אדום=unstaged, ירוק=staged)
git add .           # Stage כל הקבצים
git commit -m "Add password length validation"   # Snapshot
git push origin main  # שלח ל-GitHub
  • א. תכונות שנשמטו בטעות מן התכנון המקורי: הנחה שגויה, אף שמחוץ-לתחום הוא החלטה מכוונת ולא השמטה בשגגה כלשהי
  • ב. באגים ידועים שטרם תוקנו בקוד הקיים: הנחה שגויה, אף שמחוץ-לתחום מגדיר תכולה ואינו רשימת תקלות פתוחות כלשהי
  • ג. התיעוד הנלווה של הפרויקט כולו: הנחה שגויה, אף שמחוץ-לתחום מגדיר מה לא יפותח כעת ואינו קשור לתיעוד כלל כאן
  • ד. דברים שהוחלט במכוון לא לממש בגרסה הנוכחית, מה שמגדיר את גבולות הפרויקט וממקד את מאמצי הפיתוח כולם כאן
תשובה: ד

✓ תשובה: ד) — Out of Scope הוא מכוון, לא שגגה. "הוחלט לא לממש כרגע" = scope management. בדוגמת Password Analyzer: GUI, חיבור ל-DB, HIBP API — כולם Out of Scope ב-V1.

שאלה 4.1 · מה ההבדל בין git add לבין git commit?

  • א. שתי הפקודות זהות לחלוטין בפעולתן: הנחה שגויה, אף ש-add מכין את השינויים ו-commit שומר אותם בהיסטוריה כאן
  • ב. add מעלה ל-GitHub ו-commit שומר מקומית: הנחה שגויה, אף ששתיהן מקומיות ורק push מעלה בפועל לשרת המרוחק כאן
  • ג. git add מעביר שינויים לאזור ההיערכות, ואילו git commit מצלם את הקבצים המוכנים ושומר אותם בהיסטוריה כאן
  • ד. commit שקול ל-add ואחריו push: הנחה שגויה, אף ש-commit שומר מקומית בלבד ואינו מעלה כלל לשרת המרוחק בעצמו
תשובה: ג

✓ תשובה: ג) — Workflow: עבוד → git add . (stage) → git commit -m "message" (snapshot) → git push (GitHub). שלושה שלבים נפרדים.

שאלה 4.2 · מה .gitignore? ציין 3 סוגי קבצים שתמיד צריך להוסיף.

  • א. קובץ המגדיר את הענף הראשי של המאגר: הנחה שגויה, אף ש-gitignore מגדיר קבצים להתעלמות ואינו מגדיר ענפים כלל
  • ב. קובץ המגדיר אילו קבצים Git לא יעקוב אחריהם, ובהם תיקיית venv ותיקיית __pycache__ וקובץ env עם הסיסמאות
  • ג. קובץ המכיל את היסטוריית ה-commits: הנחה שגויה, אף ש-gitignore מגדיר התעלמות מקבצים ואינו שומר היסטוריה כלל
  • ד. קובץ הגדרות Git הגלובלי של המשתמש: הנחה שגויה, אף ש-gitignore הוא מקומי למאגר ומגדיר קבצים להתעלמות מהם כאן
תשובה: ב

✓ תשובה: ב) — .gitignore מגדיר מה Git מתעלם ממנו. חיוני: venv/ (גדול ולא portable), __pycache__/ (compiled files), .env (אל תעלה סיסמאות ו-API Keys לGitHub אף פעם!).

שאלה 4.3 · מה if __name__ == "__main__": אומר? למה חשוב?

# יצירה
cd my_project
python3 -m venv venv

# הפעלה
source venv/bin/activate      # Linux/Mac
venv\Scripts\activate         # Windows
# ה-prompt: (venv) user@machine:~/my_project$

# התקנת ספריות
pip install requests pytest

# שמירת רשימה
pip freeze > requirements.txt  # "תעודת הזהות" של הסביבה

# כשמישהו מוריד הפרויקט
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt   # מתקין הכל בבת-אחת

# ביטול
deactivate
  • א. נועד לתת שם לקובץ הנוכחי בלבד: הנחה שגויה, אף שהתנאי קובע מתי הקוד ירוץ ואינו קשור כלל לשם הקובץ עצמו כאן
  • ב. נועד להגדיר את פונקציית ה-main: הנחה שגויה, אף שהתנאי שולט במועד ההרצה ואינו מגדיר את הפונקציה עצמה כלל כאן
  • ג. הקוד ירוץ רק כשהקובץ מורץ ישירות, וכשהוא מיובא כמודול הבלוק אינו רץ ולכן הקוד ניתן לבדיקה נוחה יותר כאן
  • ד. נועד לייבא מודולים נוספים אל הקובץ: הנחה שגויה, אף שהתנאי בודק הרצה ישירה ואינו מבצע כל ייבוא של מודולים כאן
תשובה: ג

✓ תשובה: ג) — __name__ == "__main__" רק בהרצה ישירה (python main.py). כשמיובא: __name__ = שם המודול (לא "__main__") → הבלוק לא רץ, ו-main() לא נקרא. חיוני לבדיקות: אפשר לייבא פונקציות מ-main.py בלי שהקוד הראשי ירוץ.

שאלה 6.1 · מה הפקודה הנכונה ליצירת venv חדש ב-Python 3?

  • א. python3 -m venv venv: הדגל -m מריץ מודול, המילה venv היא שם המודול, והמילה השנייה היא שם תיקיית הסביבה
  • ב. pip create venv: הנחה שגויה ש-pip יוצר סביבה וירטואלית, אף שהפקודה הנכונה היא python3 עם המודול venv כאן כן
  • ג. virtualenv --new: הנחה שגויה שזו הפקודה הסטנדרטית ליצירה, אף שהמודול המובנה נקרא python3 עם venv בתחביר כאן
  • ד. python3 venv init: הנחה שגויה שקיימת תת-פקודה בשם init, אף שהתחביר הנכון הוא python3 -m venv venv בלבד כאן
תשובה: א

✓ תשובה: א) python3 -m venv venv — הפקודה הסטנדרטית. -m = run as module. venv = שם המודול. venv (השני) = שם תיקיית הסביבה.

שאלה 6.2 · מה pip freeze > requirements.txt עושה? למה חשוב?

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

✓ תשובה: ב) — requirements.txt = "תעודת זהות" הסביבה. כל שורה: package_name==version. חשוב לשיתוף: מי שמוריד הפרויקט יכול לשחזר בדיוק את אותה סביבה.

שאלה 6.3 · מה Single Responsibility Principle (SRP)?

import re
from .policy import PasswordPolicy

class PasswordAnalyzer:
    """מנתח חוזק סיסמאות — orchestrates הניתוח."""

    def __init__(self):
        self._policy = PasswordPolicy.get_instance()

    def analyze(self, password: str):
        scores, suggestions = {}, []

        # בדיקת אורך
        length = len(password)
        if length >= 16: scores["length"] = 30
        elif length >= 12: scores["length"] = 20
        elif length >= 8:
            scores["length"] = 10
            suggestions.append("Increase length to 12+")
        else:
            scores["length"] = 0
            suggestions.append(f"Too short ({length} chars). Use 12+.")

        # בדיקת סוגי תווים
        char_checks = {
            "uppercase": (r"[A-Z]", 15, "Add uppercase letters"),
            "lowercase": (r"[a-z]", 15, "Add lowercase letters"),
            "digits":    (r"\d",    15, "Add numbers"),
            "special":   (r"[!@#$%^&*]", 25, "Add special characters"),
        }
        for key, (pattern, pts, msg) in char_checks.items():
            if re.search(pattern, password): scores[key] = pts
            else: scores[key] = 0; suggestions.append(msg)

        # בדיקת סיסמאות נפוצות
        if self._policy.is_common(password):
            scores["uniqueness"] = -50
            suggestions.insert(0, "⚠️ Common password!")

        total = max(0, sum(scores.values()))
        return PasswordReport(password, total, suggestions)
  • א. כל מחלקה צריכה להכיל מתודה אחת בלבד: הנחה שגויה, אף שהעיקרון עוסק באחריות אחת בלבד ולא במספר המתודות שבה כאן
  • ב. מותרת ירושה יחידה בלבד בין מחלקות: הנחה שגויה, אף שהעיקרון עוסק בהפרדת אחריות בין מחלקות ולא במבנה הירושה כאן
  • ג. כל פונקציה צריכה להחזיר ערך אחד בלבד: הנחה שגויה, אף שהעיקרון עוסק באחריות היחידה של המחלקה ולא בערך המוחזר
  • ד. כל מחלקה או פונקציה אחראית על דבר אחד בלבד, כך שהמנתח מנתח, מחלקת החוקים מחזיקה חוקים והדוח מציג בנפרד
תשובה: ד

✓ תשובה: ד) SRP — מ-SOLID Principles. הפרדת אחריויות מפשטת קריאה, בדיקה, תחזוקה. 4 classes קטנים עם 5 methods > class אחד ענק עם 20 methods.

גאמא סגנון א׳ — ראיון · מראיין שואל: "מה ה-Big-O של blacklist check בפרויקט שלך?" — מה התשובה הנכונה ב-Password Analyzer?

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

✓ תשובה: א) O(1) — set ב-Python = hash table. חיפוש (x in s) = O(1) amortized. לעומת list שחיפוש = O(n). זו בדיוק שאלת ראיון קלאסית — "למה בחרת set ולא list?"

גאמא סגנון ב׳ — Git · מה הפקודה ליצירת branch חדש ומעבר אליו בבת-אחת?

  • א. git checkout -b feature/x: הדגל -b יוצר ענף חדש ומיד עובר אליו, שקול לצירוף של branch ולאחריו checkout
  • ב. git branch feature/x: הנחה שגויה שהפקודה גם עוברת לענף החדש, אף שהיא יוצרת ענף בלבד וללא מעבר אליו כלל כאן
  • ג. git new feature/x: הנחה שגויה שקיימת תת-פקודה בשם new, אף שהתחביר הנכון ליצירה ומעבר הוא checkout עם -b כאן
  • ד. git create feature/x: הנחה שגויה שקיימת תת-פקודה בשם create, אף שיצירה ומעבר בבת-אחת נעשים ב-checkout עם -b
תשובה: א

✓ תשובה: א) git checkout -b feature/x — -b = new branch + checkout = עבור אליו. שקול ל: git branch feature/x && git checkout feature/x. בgit החדש: git switch -c feature/x.

גאמא סגנון ג׳ — ארכיטקטורה · מראיין שואל: "למה 3 Classes (Analyzer/Policy/Report) ולא class אחד?" — מה העיקרון?

def compare_versions(a: str, b: str) -> int:
    pa = [int(x) for x in a.split('.')]
    pb = [int(x) for x in b.split('.')]
    n = max(len(pa), len(pb))
    pa += [0] * (n - len(pa)); pb += [0] * (n - len(pb))   # חלקים חסרים = 0
    return (pa > pb) - (pa < pb)
  • א. יותר מחלקות פשוט נראות מרשימות יותר: הנחה שגויה, אף שהסיבה האמיתית היא הפרדת אחריות ולא רושם חיצוני כלשהו כאן
  • ב. עקרון האחריות היחידה, שכן כל מחלקה עושה דבר אחד בלבד ולכן קל יותר לבדוק ולהרחיב את הקוד של כל מחלקה בנפרד
  • ג. שפת Python מחייבת חלוקה למחלקות נפרדות: הנחה שגויה, אף שזו החלטת תכנון של הפרדת אחריות ואינה כפייה של השפה כאן
  • ד. מדובר במימוש של תבנית ה-Singleton: הנחה שגויה, אף שהחלוקה נובעת מעקרון האחריות היחידה ולא מתבנית Singleton כאן
תשובה: ב

✓ תשובה: ב) SRP — Single Responsibility Principle מ-SOLID. כל class = אחריות אחת. יתרון: אם Report ישתנה, Analyzer לא נשבר. קל לכתוב test לכל class בנפרד.