Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

دوال Static و Class

جزء من قسم Object Oriented Programming في رحلة Python على Coddy — الدرس 39 من 64.

بالإضافة إلى طرق المثيل (instance methods) العادية، يمكن أن تحتوي الأصناف على طرق ثابتة (static methods) وطرق صنف (class methods) تخدم أغراضاً مختلفة.

إليك مثال على دالة ثابتة (static method):

class MathHelper:
    @staticmethod
    def add(a, b):
        return a + b
    
    @staticmethod
    def is_even(number):
        return number % 2 == 0

لا تحتاج الطرق الساكنة (Static methods) إلى self وتعمل مثل الدوال العادية. قم باستدعائها مباشرة من الفئة (class):

result = MathHelper.add(5, 3)
print(result)

check = MathHelper.is_even(10)
print(check)

إليك مثال على class method:

class Person:
    count = 0  # متغير الفئة
    
    def __init__(self, name):
        self.name = name
        Person.count += 1
    
    @classmethod
    def get_count(cls):
        return cls.count
    
    @classmethod
    def create_anonymous(cls):
        return cls("Anonymous")

تتلقى دوال الفئة الفئة نفسها (cls) كمعامل أول:

person1 = Person("Alice")
person2 = Person("Bob")
print(Person.get_count())  # 2

استخدم أساليب الفئة (class methods) كمنشئات بديلة:

anonymous = Person.create_anonymous()
print(anonymous.name)      # Anonymous
print(Person.get_count())  # 3

قارن بين جميع أنواع الطرق الثلاثة في فئة (class) واحدة:

class Calculator:
    brand = "Python Calc"
    
    def __init__(self, owner):
        self.owner = owner
    
    # طريقة مثيل - تحتاج self، وتصل إلى بيانات المثيل
    def show_owner(self):
        return f"Owned by {self.owner}"
    
    # طريقة فئة - تحتاج cls، وتصل إلى بيانات الفئة
    @classmethod
    def get_brand(cls):
        return cls.brand
    
    # طريقة ثابتة - لا تحتاج لأي منهما، مجرد دالة مساعدة
    @staticmethod
    def multiply(x, y):
        return x * y
calc = Calculator("Alice")
print(calc.show_owner())        # مملوك بواسطة Alice
print(Calculator.get_brand())   # حاسبة بايثون
print(Calculator.multiply(4, 5)) # 20

المخرجات:

8
True
2
Anonymous
3
Owned by Alice
Python Calc
20

يمكنك استدعاء أساليب الفئة (class methods) والأساليب الثابتة (static methods) من المثيلات (instances) أيضاً:

calc = Calculator("Bob")
print(calc.get_brand())      # Python Calc
print(calc.multiply(2, 3))   # 6

الاختلافات الرئيسية:

  • أساليب النسخة (Instance methods): تحتاج إلى self، وتصل إلى بيانات النسخة
  • أساليب الفئة (Class methods): تحتاج إلى cls، وتصل إلى بيانات الفئة، وهي جيدة للمنشئات البديلة
  • الأساليب الثابتة (Static methods): لا تحتاج لأي منهما، وهي مجرد وظائف مساعدة مرتبطة بالفئة

نقطة رئيسية: استخدم @staticmethod للوظائف المساعدة التي تنتمي منطقياً إلى الفئة ولكنها لا تحتاج إلى بيانات الفئة أو النسخة. استخدم @classmethod عندما تحتاج إلى الوصول إلى الفئة نفسها، مثل المنشئات البديلة أو الوصول إلى متغيرات الفئة.

challenge icon

التحدي

سهل

في هذا التحدي، ستقوم بتنفيذ فئة Temperature بوظائف محددة مع الاستفادة من إطار عمل اختبار شامل.

تحتاج إلى تعديل ملف temperature.py فقط، باتباع تعليقات TODO التي توجه عملية التنفيذ الخاصة بك. يجب أن تتضمن الفئة ما يلي:

  • متغير فئة (class variable) لتتبع قراءات درجات الحرارة
  • طريقة ثابتة (static method) لتحويل درجة الحرارة
  • طرق فئة (class methods) لإضافة القراءات وحساب المتوسطات

ورقة مرجعية

يمكن أن تحتوي الأصناف (Classes) على ثلاثة أنواع من الدوال (methods): دوال النسخة (instance methods)، والدوال الساكنة (static methods)، ودوال الصنف (class methods).

تستخدم الدوال الساكنة (Static methods) المزخرف @staticmethod، ولا تحتاج إلى self، وتعمل مثل الدوال العادية:

class MathHelper:
    @staticmethod
    def add(a, b):
        return a + b

# الاستدعاء مباشرة من الصنف
result = MathHelper.add(5, 3)

تستخدم دوال الصنف (Class methods) المزخرف @classmethod وتستقبل الصنف (cls) كمعامل أول:

class Person:
    count = 0
    
    def __init__(self, name):
        self.name = name
        Person.count += 1
    
    @classmethod
    def get_count(cls):
        return cls.count
    
    @classmethod
    def create_anonymous(cls):
        return cls("Anonymous")

# الاستخدام
print(Person.get_count())
anonymous = Person.create_anonymous()

الاختلافات الرئيسية:

  • دوال النسخة (Instance methods): تحتاج إلى self، وتصل إلى بيانات النسخة
  • دوال الصنف (Class methods): تحتاج إلى cls، وتصل إلى بيانات الصنف، وهي مفيدة للمنشئات البديلة (alternative constructors)
  • الدوال الساكنة (Static methods): لا تحتاج لأي منهما، وهي مجرد دوال مساعدة مرتبطة بالصنف

يمكن استدعاء كل من دوال الصنف والدوال الساكنة من الصنف نفسه أو من النسخ:

Calculator.multiply(4, 5)  # من الصنف
calc.multiply(4, 5)        # من النسخة

جرّب بنفسك

from temperature import Temperature

# معالج حالات الاختبار للاختبار الشامل
test_case = input()

if test_case == "default_test":
    # اختبار الوظائف الأساسية
    Temperature.celsius_readings = []
    Temperature.add_reading(25)
    Temperature.add_reading(30)
    Temperature.add_reading(27)
    
    print(f"Average reading: {Temperature.average_reading()}")
    print(f"22°C is {Temperature.celsius_to_fahrenheit(22)}°F")

elif test_case == "empty_readings":
    # اختبار متوسط القراءة بدون وجود قراءات
    Temperature.celsius_readings = []
    print(f"Average reading: {Temperature.average_reading()}")

elif test_case == "single_reading":
    # الاختبار بقراءة واحدة فقط
    Temperature.celsius_readings = []
    Temperature.add_reading(100)
    print(f"Average reading: {Temperature.average_reading()}")

elif test_case == "negative_values":
    # الاختبار بقيم درجات حرارة سالبة
    Temperature.celsius_readings = []
    Temperature.add_reading(-10)
    Temperature.add_reading(-20)
    Temperature.add_reading(-30)
    print(f"Average reading: {Temperature.average_reading()}")
    print(f"-15°C is {Temperature.celsius_to_fahrenheit(-15)}°F")

elif test_case == "zero_value":
    # الاختبار بقراءة 0 درجة مئوية
    print(f"0°C is {Temperature.celsius_to_fahrenheit(0)}°F")

elif test_case == "extreme_values":
    # الاختبار بقيم درجات حرارة قصوى
    absolute_zero = -273.15  # الصفر المطلق بالدرجة المئوية
    sun_surface = 5500  # درجة حرارة سطح الشمس التقريبية بالدرجة المئوية
    
    print(f"{absolute_zero}°C is {Temperature.celsius_to_fahrenheit(absolute_zero)}°F")
    print(f"{sun_surface}°C is {Temperature.celsius_to_fahrenheit(sun_surface)}°F")

elif test_case == "reset_readings":
    # اختبار إعادة ضبط القراءات
    Temperature.celsius_readings = []
    Temperature.add_reading(10)
    Temperature.add_reading(20)
    Temperature.add_reading(30)
    print(f"Average reading before reset: {Temperature.average_reading()}")
    
    Temperature.celsius_readings = []
    print(f"Average reading after reset: {Temperature.average_reading()}")

elif test_case == "decimal_values":
    # الاختبار بقيم عشرية
    Temperature.celsius_readings = []
    Temperature.add_reading(36.5)  # درجة حرارة الجسم الطبيعية
    Temperature.add_reading(37.2)  # حمى خفيفة
    Temperature.add_reading(36.9)  # تباين طبيعي
    print(f"Average reading: {Temperature.average_reading()}")

elif test_case == "many_readings":
    # الاختبار بقراءات عديدة
    Temperature.celsius_readings = []
    for i in range(1, 101):
        Temperature.add_reading(i)
    print(f"Average reading with 100 values: {Temperature.average_reading()}")
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming