دوال 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 * ycalc = 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 عندما تحتاج إلى الوصول إلى الفئة نفسها، مثل المنشئات البديلة أو الوصول إلى متغيرات الفئة.
التحدي
سهلفي هذا التحدي، ستقوم بتنفيذ فئة 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()}")يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsمعامل selfMethodsAttributesدالة البناء (__init__)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةدالة ()superإعادة تعريف الدوال (Method Overriding)الوراثة المتعددةترتيب استدعاء الدوال (Method Resolution Order)مراجعة - هيكلية الموظفين5تعدد الأشكال
مراجعة إعادة تعريف الدوالمفهوم Duck Typingالأصناف والدوال المجردةتصميم الواجهاتملخص - حاسبة الأشكال8مفاهيم OOP المتقدمة
التركيب مقابل الوراثةMixinsدوال Static و Classمزخرفات Classمديرو السياق (Context Managers)