Kodax

الدوال السحرية (Magic Methods) — البرمجة الكائنية

الدوال السحرية (Magic / Dunder Methods)

الدوال التي يبدأ اسمها وينتهي بشرطتين سفليتين (__init__, __str__) تسمى dunder methods. لا تستدعيها بنفسك عادة؛ Python تستدعيها تلقائياً عند استخدام العمليات المدمجة. بتعريفها تجعل أصنافك تتصرف مثل الأنواع المدمجة.

التمثيل النصي

| الدالة | متى تُستدعى | لمن |

|---|---|---|

| __str__ | print(obj)، str(obj) | للمستخدم: نص مقروء |

| __repr__ | في الطرفية، داخل القوائم، repr(obj) | للمبرمج: يوضح كيف أُنشئ الكائن |

def __repr__(self):
    return f"Money({self.amount!r}, {self.currency!r})"

إذا عرّفت واحدة فقط، فعرّف __repr__: Python تستخدمها بديلاً عن __str__.

المقارنة

def __eq__(self, other):
    if not isinstance(other, Money):
        return NotImplemented
    return (self.amount, self.currency) == (other.amount, other.currency)

بدون __eq__، تقارن == هوية الكائنين (هل هما نفس الكائن في الذاكرة) وليس محتواهما. إرجاع NotImplemented يخبر Python أن هذه المقارنة غير مدعومة لهذا النوع.

__lt__ (أصغر من) تجعل sorted() و min() تعمل مع كائناتك.

العمليات الحسابية

def __add__(self, other):
    return Money(self.amount + other.amount, self.currency)

الآن price + tax تعمل. يجب أن ترجع كائناً جديداً لا أن تعدّل self.

سلوك الحاويات

| الدالة | تُفعّل |

|---|---|

| __len__ | len(obj) |

| __getitem__ | obj[i] وأيضاً التكرار بـ for |

| __contains__ | x in obj |

| __iter__ | for x in obj |

__bool__

تحدد إن كان الكائن "صحيحاً" في الشروط: if cart:.

قاعدة مهمة

عرّف الدوال السحرية التي يتوقعها المستخدم منطقياً فقط. جمع نقطتين منطقي، أما جمع موظفين فغالباً ليس كذلك.

تمرين

أنشئ صنف Vector (x, y) مع __repr__ و __add__ و __eq__ و __abs__ (الطول)، وصنف Playlist يدعم len() و in والتكرار.

class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
    
    # للطباعة العادية
    def __str__(self):
        return f"{self.title} - {self.author}"
    
    # للتمثيل التقني
    def __repr__(self):
        return f"Book('{self.title}', '{self.author}', {self.pages})"
    
    # len()
    def __len__(self):
        return self.pages
    
    # المقارنة
    def __eq__(self, other):
        return self.title == other.title and self.author == other.author
    
    def __lt__(self, other):
        return self.pages < other.pages
    
    # الجمع (دمج كتابين)
    def __add__(self, other):
        return Book(
            f"{self.title} & {other.title}",
            f"{self.author}, {other.author}",
            self.pages + other.pages
        )

book1 = Book("Python 101", "أحمد", 200)
book2 = Book("Python 201", "محمد", 300)

print(book1)           # Python 101 - أحمد
print(repr(book1))     # Book('Python 101', 'أحمد', 200)
print(len(book1))      # 200
print(book1 < book2)   # True
print(book1 + book2)   # Python 101 & Python 201

# class يتصرف كـ list
class Playlist:
    def __init__(self, name):
        self.name = name
        self._songs = []
    
    def __len__(self):
        return len(self._songs)
    
    def __getitem__(self, index):
        return self._songs[index]
    
    def __setitem__(self, index, value):
        self._songs[index] = value
    
    def __iter__(self):
        return iter(self._songs)
    
    def __contains__(self, song):
        return song in self._songs
    
    def add(self, song):
        self._songs.append(song)

playlist = Playlist("مفضلتي")
playlist.add("أغنية 1")
playlist.add("أغنية 2")
playlist.add("أغنية 3")

print(len(playlist))      # 3
print(playlist[0])        # أغنية 1
print("أغنية 2" in playlist)  # True

for song in playlist:
    print(song)
اختبر فهمك