الوظائف الخاصة في بيثون 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>})
"""
قائمة الوظائف الخاصة تجدها عبر الرابط التالي: 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
تقوم هذه الوظيفة بتهيئة الكائن عند انشائه
كود :
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
كذلك علينا اعادة تعريف وظائف المعاملات + و - و * و / لكي نتمكن من اجراء عمليات حسابية على كائناتنا من نوع 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)
لعرض العدد المركب على الشاشة (النتيجة المعروضة ستكون مشابهة لما تفعله الدالة 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)
عملية الجمع: للتذكير جمع عددين مركبين يتم عبر جمع الجزئين الحقيقيين من جهة وجمع الجزئين التخيليين من جهة ثانية:
كود :
# (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)
هنا لا فرق فعملية الجمع على الاعداد المركبة تبادلية (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)
عملية ضرب (عدد مركب * عدد مركب) أو (عدد مركب * عدد صحيح) أو (عدد مركب * عدد حقيقي)
كود :
# (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)
عملية قسمة (عدد مركب / عدد مركب) أو (عدد مركب / عدد حقيقي_صحيح)
كود :
# (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")
الوظيفة __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