تقييم الموضوع :
  • 0 أصوات - بمعدل 0
  • 1
  • 2
  • 3
  • 4
  • 5
الوظائف الخاصة في بيثون special methods الجزء الأول
#1
الوظائف الخاصة في بيثون special methods الجزء الأول
رأينا كيف نُعَرِفُ الوظائف داخل الفئات، وهو أمر طبيعي تماما في بناء الفئات، حيث نستخدمها في معالجة الأمثلة. لكننا ذكرنا أيضا أننا نستطيع إنشاء كائناتنا الخاصة والتي ستتصرف تماما كالكائنات المدمجة في بيثون.

بادئ ذي بدء، ولأن موضوعنا الحالي لا يتحدث عن التوريث في بيثون، لا بأس أن نعتبر المعلومة التالية كمسلّمة في الوقت الحالي: جميع الفئات التي نبنيها ترث تلقائيا من الكلاس الأم object. في هذه الكلاس تم تعريف الوظائف الخاصة التي راينا بعضها سابقا وسنرى بعضها في هذا الموضوع وفي مواضيع لاحقة ان شاء الله (تلك التي تكون بهذا الشكل __xyz__()). العديد منها لها تصرف قياسي اذا لم نقم باعادة تعريفها، ذلك التصرف القياسي قد يكون مناسبا، لكننا أحيانا نحتاج الى اعادة تعريف بعضها لتتماشى مع الفئة التي نريد بناءها، وهذا ما يسمى redefinition او operator overloading بالنسبة للمعاملات. انظر:

كود :
class MyClass:
   pass

issubclass(MyClass, object)     # True
c = MyClass()
isinstance(c, object)           # True
vars(MyClass)                   # مجال تسمية الكلاس يحتوي على نوع خاص من القواميس
"""
mappingproxy({'__doc__': None, '__dict__': <attribute '__dict__' of 'MyClass' objects>,
'__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'MyClass' objects>})
"""
عندما تكون احدى الوظائف الخاصة مضبوطة على None فهذا يعني أنها غير معرفة.

قائمة الوظائف الخاصة تجدها عبر الرابط التالي: https://docs.python.org/3.6/reference/da...thod-names

بما أنني من أنصار فكرة أن أفضل طريقة للتعلم هي التطبيق المباشر، فسنبني كلاس الاعداد المركبة (complex numbers)، لكنني اذكر اولا بوجود نوع للاعداد المركبة مدمج في لغة بيثون (Complex). هدفنا ليس تطويره ولا استبداله، أبعد ما يكون عن ذلك، لكنني رايت أنه مثال جيد لفهم بعض الوظائف الخاصة موضوع حديثنا الحالي.

حيث يوجد العشرات من الوظائف الخاصة ولا يمكن تطبيقها كلها على نوع واحد من الكائنات، سنخصص مثالنا التطبيقي الاول للتعرف على بعض العمليات الحسابية أساسا بالاضافة الى بعض عمليات العرض والطباعة على الشاشة مثلما يلي:

كود :
c1 = ComplexNumber(2, 3)       # 2+3i
c2 = ComplexNumber(1, -4)      # 1-4i
c3 = c1 + c2                   # 3-1i
c3                             # 3-1i
print(c3)                      # 3-1i
str(c3)                        # '3-1i'
c1 - c2                        # 1+7i
c1 * c2                        # 14-5i
c1 * 10.5                      # 21+31.5i
10.5 * c1                      # 21+31.5i
c4 = ComplexNumber(-5, 2)
c5 = c1 + c2 - c3 * c4         # 16-12i
print(c5)                      # 16-12i
الوظيفة __init__

تقوم هذه الوظيفة بتهيئة الكائن عند انشائه

كود :
class ComplexNumber:
   def __init__(self, real, imaginary):
       """
       This method is used to initialize an instance.
       It is called after the instance is created.
       """
       self.real=real
       self.imaginary=imaginary
لنلاحظ اولا قبل اعادة تعريف بعض الوظائف، على ماذا سنحصل عندما نطلب طباعة كائن مثيل أو جمع كائنين:

كود :
c = ComplexNumber(5, -3)
cc = ComplexNumber(2, 7)
print(c)                    # <__main__.ComplexNumber object at 0x7feea8a19128>
c                           # <__main__.ComplexNumber object at 0x7feea8a19128>
c + cc                      # TypeError
لكننا نريد طباعة القيمة التي يشير اليها المتغير، لذلك علينا اعادة تعريف __str__ و __repr__

كذلك علينا اعادة تعريف وظائف المعاملات + و - و * و / لكي نتمكن من اجراء عمليات حسابية على كائناتنا من نوع ComplexNumber

الوظيفة __str__

لطباعة العدد المركب على الشاشة بواسطة print

كود :
   def __str__(self):
       """This method displays our object (complex number)
       using the method str(objet)
       """
       R = self.real
       I = self.imaginary
       i = 'i'
       S = '+'
       # we treat all cases to have a more beautiful display
       if R == 0 and I == 0:return '0i'
       if I==0:I=S=i=''
       if R==0:R=S=''
       if self.imaginary<0:
           S='-'
           I=abs(self.imaginary)
       
       return '{}{}{}{}'.format(R,S,I,i)
الوظيفة __repr__

لعرض العدد المركب على الشاشة (النتيجة المعروضة ستكون مشابهة لما تفعله الدالة print)

كود :
   def __repr__(self):
       """This method displays our object (complex number)
       using the method str(objet)
       """
       R = self.real
       I = self.imaginary
       i = 'i'
       S = '+'
       # we treat all cases to have a more beautiful display
       if R == 0 and I == 0:return '0i'
       if I==0:I=S=i=''
       if R==0:R=S=''
       if self.imaginary<0:
           S='-'
           I=abs(self.imaginary)
       
       return '{}{}{}{}'.format(R,S,I,i)
الوظيفتان __add__ و __radd__

عملية الجمع: للتذكير جمع عددين مركبين يتم عبر جمع الجزئين الحقيقيين من جهة وجمع الجزئين التخيليين من جهة ثانية:

كود :
   # (a+bi) + (c+di) = (a+b) + (c+d)i
   # (3+5i) + (4-2i) = (3+4) + (5-2)i = 7+3i
   def __add__(self, other):
       """This method adds two complex numbers using '+'"""
       R = self.real + other.real
       I = self.imaginary + other.imaginary
       return ComplexNumber(R, I)
   def __radd__(self, other):
       """This method adds two complex numbers using '+'"""
       return self.__add__(other)
ما الفرق بين __add__ و __radd__ ؟

هنا لا فرق فعملية الجمع على الاعداد المركبة تبادلية (commutative <=> a+b = b+a)، لذلك أحلت الوظيفة الثانية الى الأولى ببساطة، لكن بالامكان استخدام العلامة '+' لتجميع كائنات غير عددية (نصية مثلا) وعند ذلك يجب اعتماد تمشي مختلف

الوظيفة __sub__

عملية الطرح

كود :
   # (a+bi) - (c+di) = (a+b) - (c+d)i
   # (3+5i) - (4-2i) = (3+4) - (5-2)i = -1+7i
   def __sub__(self, other):
       """This method subtracts a complex number from another using '-'"""
       R = self.real - other.real
       I = self.imaginary - other.imaginary
       return ComplexNumber(R, I)
الوظيفتان __mul__ و __rmul__

عملية ضرب (عدد مركب * عدد مركب) أو (عدد مركب * عدد صحيح) أو (عدد مركب * عدد حقيقي)

كود :
   # (a+bi) * (c+di) = (ac - bd) + (ad + bc)i
   # (3+5i) * (4-2i) = (3*4 - 5*(-2)) + (3*(-2) + 5*4)i = 22+14i
   # (3+5i) * 10.5    = (3*10.5) + (5*10.5)i  = 21+31.5i
   def __mul__(self, other):
       """This function performs the multiplication of two complex numbers
       or the multiplication of a complex number with a real number using '*'"""
       if isinstance(other, ComplexNumber):
           a = self.real
           b = self.imaginary
           c = other.real
           d = other.imaginary
           R = a*c - b*d
           I = a*d + b*c
           return ComplexNumber(R, I)
       elif isinstance(other, (int, float)):
           R = self.real * other
           I = self.imaginary * other
           return ComplexNumber(R, I)
       else:
           raise TypeError("Incompatible type")
   def __rmul__(self, other):
       """This function performs the multiplication of two complex numbers
       or the multiplication of a complex number with a real number using '*'"""
       return self.__mul__(other)
الوظيفة __truediv__

عملية قسمة (عدد مركب / عدد مركب) أو (عدد مركب / عدد حقيقي_صحيح)

كود :
   # (a+bi) / (c+di) = (ac + bd) / (c*c + d*d) + (cb - ad)i / (c*c + d*d)
   # (3+5i) / (4-2i) = 0.1+1.3i
   def __truediv__(self, other):
       """This function performs the multiplication of two complex numbers
       or the multiplication of a complex number with a real number using '*'"""
       if isinstance(other, ComplexNumber):
           a = self.real
           b = self.imaginary
           c = other.real
           d = other.imaginary
           if c == 0 and d == 0: raise ZeroDivisionError("complex division by zero")
           R = (a*c + b*d) / (c*c + d*d)
           I = (c*b - a*d) / (c*c + d*d)
           return ComplexNumber(R, I)
       elif isinstance(other, (int, float)):
           if other == 0: raise ZeroDivisionError("complex division by zero")
           R = self.real / other
           I = self.imaginary / other
           return ComplexNumber(R, I)
       else:
           raise TypeError("Incompatible type")
وظائف أخرى يمكن اعادة تعريفها على كلاس ComplexNumber

الوظيفة __pow__     :  للرفع الى الاس (لك عزيزي القارئ أن تقوم بتعريفها استنادا على __mul__)

الوظيفة __rpow__    :  للرفع الى الاس

الوظيفة __iadd__    :   +=  ربما كان يجدر بنا البدء باعادة تعريف هذه الوظيفة ومثيلاتها

                           وانطلاقا منها نعيد تعريف الجمع خاصة اذا كنا نبحث عن الفاعلية

                           والسرعة، فهذه الوظيفة تعمل in place أي على الكائن نفسه self.

الوظيفة __isub__    :   -=

الوظيفة __imul__    :   *=

الوظيفة __itruediv__:   /=

الوظيفة __neg__     :   -س (س هو العدد المركب)

الوظيفة __pos__     :   +س (س هو العدد المركب)

الوظيفة __del__     :   لحذف عدد مركب

الوظيفة __getattr__ :   اطلاق استثناء AttributeError مثلا اذا لم تكن السمة موجودة



في هذا الموضوع تعرفنا على بعض الوظائف الخاصة وقمنا باعادة تعريفها لتعمل مع الكلاس ComplexNumber، لكن يوجد عديد الوظائف الاخرى مما لا يمكن او ليس من المنطقي تعريفها: هل يمكن مقارنة الاعداد المركبة مثلا؟ لذلك لنا عودة ان شاء الله لنتحدث عن المزيد من الوظائف الخاصة في مواضيع لاحقة.

اذا وجدت صعوبة أو أخطاء عند نسخ الكود البرمجي للكلاس ComplexNumber فهي في الملحقات في شكل ملف مضغوط (لا يسمح المنتدى برفع ملفات .py مباشرة). لتجربتها افتحها ثم نفذها (F5) بواسطة IDLE أو انتقل بالطرفية الى المجلد الذي حفظت فيه الملف ونفذ في مفسر بيثون :

كود :
from complex_number import ComplexNumber
ثم جرب عليها أمثلة كما بيناه في اول الموضوع ونعيده هنا:

كود :
c1 = ComplexNumber(2, 3)       # 2+3i
c2 = ComplexNumber(1, -4)      # 1-4i
c3 = c1 + c2                   # 3-1i
c3                             # 3-1i
print(c3)                      # 3-1i
str(c3)                        # '3-1i'
c1 - c2                        # 1+7i
c1 * c2                        # 14-5i
c1 * 10.5                      # 21+31.5i
10.5 * c1                      # 21+31.5i
c4 = ComplexNumber(-5, 2)
c5 = c1 + c2 - c3 * c4         # 16-12i
print(c5)                      # 16-12i


الملفات المرفقة
.zip   complex_number.zip (الحجم : 1,010 bytes / التحميلات : 2)
الرد


التنقل السريع :


مستخدمين يتصفحوا هذا الموضوع: 1 ضيف