לדלג לתוכן

9.2 פונקציות ומחלקות הרצאה

פונקציות

  • מה הן פונקציות טובות?

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

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

def process_data(data):
    valid_numbers = []
    for item in data:
        if isinstance(item, int):
            valid_numbers.append(item)

    average = calculate_average(valid_numbers)
    print_result(average)

def calculate_average(numbers):
    total_sum = sum(numbers)
    count = len(numbers)
    return total_sum / count if count != 0 else 0

def print_result(average):
    print("The average is:", average)

# Example usage
data = [10, 20, 30, "hello", 40, 50]
process_data(data)

- פונקציה טובה
def process_data(data):
    valid_numbers = extract_valid_numbers(data)
    average = calculate_average(valid_numbers)
    print_result(average)

def extract_valid_numbers(data):
    return [item for item in data if isinstance(item, int)]

def calculate_average(numbers):
    total_sum = sum(numbers)
    count = len(numbers)
    return total_sum / count if count != 0 else 0

def print_result(average):
    print("The average is:", average)

# Example usage
data = [10, 20, 30, "hello", 40, 50]
process_data(data)

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

def print_result(average):
    print("The average is:", average)

def extract_valid_numbers(data):
    return [item for item in data if isinstance(item, int)]

def calculate_average(numbers):
    total_sum = sum(numbers)
    count = len(numbers)
    return total_sum / count if count != 0 else 0

def process_data(data):
    valid_numbers = extract_valid_numbers(data)
    average = calculate_average(valid_numbers)
    print_result(average)

- קוד טוב:
def process_data(data):
    valid_numbers = extract_valid_numbers(data)
    average = calculate_average(valid_numbers)
    print_result(average)

def extract_valid_numbers(data):
    return [item for item in data if isinstance(item, int)]

def calculate_average(numbers):
    total_sum = sum(numbers)
    count = len(numbers)
    return total_sum / count if count != 0 else 0

def print_result(average):
    print("The average is:", average)

פונקציות צריכות להיות קטנות וקצרות: ככל שהפונקציה יותר קצרה, כך זה יותר טוב - פונקציות כמעט לעולם לא יהיו מעל 20 שורות קוד, דאגו שכל הפונקציות שלכם יהיו עד 10-15 שורות קוד - אם הפונקציה שלכם ארוכה ואתם לא מצליחים לקצר אותה, נשמע שהפונקציה שלכם עושה יותר מדבר אחד, חלקו את הפונקציה למספר פונקציות.

אל תשתמשו בmatch statements: כאשר תרצו להוסיף קוד match, הוא סתם יתחיל להיות גדול ויגרום לפונקציה להיות ענקית, זה לא טוב. אז במקום match נשתמש בpolymorphism בoop - ראו את הדוגמה:
- קוד לא טוב:

def calculate_area(shape_type, argument):
    match shape_type:
        case "square":
            return argument*argument
        case "circle":
            return argument*argument*3.14
        case _:
            return 0

print("Area of Circle:", calculate_area("circle", 3))
print("Area of Square:", calculate_area("square", 3))

- קוד טוב:
class Shape:
    def area(self):
        pass

class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14 * self.radius * self.radius

class Square(Shape):
    def __init__(self, side_length):
        self.side_length = side_length

    def area(self):
        return self.side_length * self.side_length

def calculate_area(shape):
    return shape.area()

# Example usage
circle = Circle(5)
square = Square(4)

print("Area of Circle:", calculate_area(circle))
print("Area of Square:", calculate_area(square))

פונקציות לעולם לא צריכות לקבל מעל 3 פרמטרים: פונקציות צריכות לקבל כמה שפחות פרמטרים, אם אתם לא מצליחים, נסו לחלק את הפרמטרים שלכם לאובייקטים כלליים יותר, השתמשו בdataclass, השתמשו בdictionary שיכיל את הפרמטרים בצורה כלשהי:
- קוד לא טוב:

def make_circle(x: int, y: int, radius: int)

- קוד טוב:
@dataclass
class Point:
    x: int
    y: int

def make_circle(point: Point, radius: int)

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

def check_password(username: str, password: str) -> bool:
    res = db.users.query(username, password)
    if res:
        session.start()
        return True
    else:
        log.info("Password or username not meet!")
        return False

- קוד טוב:
def check_password(username: str, password: str) -> bool:
    return db.users.query(username, password)

def handle_login(username: str, password: str) -> None:
    if check_password(username, password):
        session.start()
    else:
        log.info("Password or username not meet!")

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

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

def handle_login(username, password):
    if check_password(username, password):
        login(username, password)
    else:
        return -1

- קוד טוב:
def handle_login(username, password):
    if check_password(username, password):
        login(username, password)
    else:
        raise AuthenticationException(message="Password or username not meet")

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

רק יציאה אחת: נסה כמה שאפשר לעשות רק return אחד, כמה שפחות break / continue - לפונקציה שכתובה טוב אמורה להיות רק יציאה אחת, לא כמה. קשה מאוד לעקוב על פונקציה שיכולה להסתיים מכמה שורות שונות

מחלקות

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