خانه / آموزش‌ها / مرجع کامل متدهای جادویی (Magic Methods) در پایتون

مرجع کامل متدهای جادویی (Magic Methods) در پایتون

🐍 HomeOfPython
|
📅 1404/10/19

سطح مقدماتی (Beginner Level)

متدهای جادویی (Magic Methods) که به آن‌ها Dunder Methods (مخفف Double Underscore) نیز گفته می‌شود، ستون فقرات برنامه‌نویسی شی‌گرا در پایتون هستند. این متدها به شما اجازه می‌دهند رفتار اشیاء کلاس خود را در تعامل با عملگرهای پایتون (مثل +، *، ==) و توابع داخلی (مثل len()، print()، str()) تعریف کنید.

در این بخش، چرخه حیات شیء و نمایش آن را بررسی می‌کنیم.

۱. چرخه حیات شیء: ساخت و مقداردهی

بسیاری از برنامه‌نویسان تصور می‌کنند __init__ سازنده (Constructor) کلاس است، اما این دقیق نیست. چرخه ساخت شامل سه مرحله است: ساختن (__new__)، مقداردهی (__init__) و حذف (__del__).

  • __new__(cls, ...): متد سازنده واقعی. اولین متدی که اجرا می‌شود و وظیفه دارد یک نمونه (Instance) از کلاس بسازد و برگرداند.
  • __init__(self, ...): متد مقداردهی اولیه. بلافاصله بعد از __new__ اجرا می‌شود تا ویژگی‌های اولیه شیء ساخته شده را تنظیم کند.
  • __del__(self): متد مخرب (Destructor). زمانی که مفسر پایتون تشخیص دهد شیء دیگر استفاده نمی‌شود (Garbage Collection)، این متد صدا زده می‌شود.

مثال ۱: مقداردهی استاندارد (The Common Case)

رایج‌ترین استفاده، تنظیم ویژگی‌ها در __init__ است.

Python

مثال ۲: الگوی Singleton با استفاده از __new__

از __new__ زمانی استفاده می‌شود که بخواهیم کنترل کنیم آیا شیء جدیدی ساخته شود یا خیر (مثلاً فقط یک نمونه از کلاس در کل برنامه وجود داشته باشد).

Python

مثال ۳: مدیریت حذف با __del__

هشدار: به زمان اجرای __del__ اعتماد کامل نکنید، اما برای لاگ‌گیری یا بستن منابع خارجی مفید است.

python
# Static Example: Resource cleanup
import socket

class NetworkHandler:
    def __init__(self):
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def __del__(self):
        self.sock.close()
        print("Socket closed automatically.")

۲. نمایش شیء: __str__ و __repr__

پایتون دو روش برای تبدیل شیء به رشته دارد. تفاوت این دو بسیار حیاتی است:

  1. __str__: هدف آن خوانایی برای کاربر نهایی است. وقتی print(obj) یا str(obj) را صدا می‌زنید.
  2. __repr__: هدف آن رفع ابهام برای برنامه‌نویس است. وقتی نام متغیر را در کنسول تایپ می‌کنید یا repr(obj) را صدا می‌زنید. قانون طلایی: خروجی repr باید در صورت امکان کدی باشد که شیء را دوباره بسازد.

مثال ۱: تفاوت نمایشی

Python

مثال ۲: دیباگ راحت‌تر با __repr__

اگر __repr__ را تعریف نکنید، خروجی پیش‌فرض چیزی شبیه <__main__.Obj object at 0x7f...> است که هیچ اطلاعات مفیدی ندارد.

Python

۳. عملگرهای مقایسه‌ای (Comparison Operators)

برای اینکه اشیاء شما قابل مرتب‌سازی (Sort) یا استفاده در شرط‌ها باشند، باید این متدها را پیاده‌سازی کنید: __eq__ (==), __ne__ (!=), __lt__ (<), __gt__ (>), __le__ (<=), __ge__ (>=).

مثال ۱: مقایسه موجودی حساب‌ها

Python

مثال ۲: مرتب‌سازی لیست اشیاء

وقتی __lt__ (کوچکتر از) را تعریف کنید، پایتون می‌تواند لیستی از اشیاء شما را sort کند.

Python

سطح پیشرفته (Professional Level)

در این سطح به سراغ تغییر رفتار عملگرهای ریاضی، مدیریت دسترسی پویا به ویژگی‌ها (Reflection)، ساخت کانتینرهای سفارشی و مدیریت حافظه می‌رویم.

۱. سربارگذاری عملگرهای ریاضی (Operator Overloading)

برای پشتیبانی کامل از ریاضیات، سه دسته متد وجود دارد:

  1. Normal (__add__): وقتی عملوند چپ، کلاس ما باشد (obj + 1).
  2. Reverse (__radd__): وقتی عملوند چپ کلاس ما را نشناسد و عملوند راست کلاس ما باشد (1 + obj).
  3. In-Place (__iadd__): برای عملیات انتسابی (obj += 1).

مثال ۱: پیاده‌سازی بردار ریاضی (Vector)

این مثال هر سه نوع عملگر را نشان می‌دهد.

Python

مثال ۲: ضرب اسکالر سفارشی

Python

۲. مدیریت دسترسی به ویژگی‌ها (Attribute Access Hooks)

پایتون به شما اجازه می‌دهد فرآیند خواندن یا نوشتن obj.attribute را کاملاً تغییر دهید. این تکنیک در ساخت ORMها و سیستم‌های RPC بسیار کاربرد دارد.

  • __getattr__(self, name): فقط زمانی صدا زده می‌شود که ویژگی پیدا نشود (Fallback).
  • __getattribute__(self, name): همیشه صدا زده می‌شود (مواظب حلقه‌های بی‌نهایت باشید!).
  • __setattr__(self, name, value): هنگام مقداردهی ویژگی.
  • __delattr__(self, name): هنگام حذف ویژگی.

مثال ۱: دیکشنری هوشمند (Dot Notation)

تبدیل دیکشنری به شکلی که بتوان با نقطه به کلیدها دسترسی داشت.

Python

مثال ۲: اعتبارسنجی هنگام مقداردهی (__setattr__)

جلوگیری از تغییر ویژگی‌های خاص یا اعتبارسنجی داده‌ها.

Python

۳. پروتکل‌های کانتینر (Container Protocols)

برای ساختن کلاسی که مثل list یا dict رفتار کند، باید متدهای زیر را پیاده‌سازی کنید:

  • __len__: طول شیء.
  • __getitem__: دسترسی با ایندکس obj[key].
  • __setitem__: مقداردهی با ایندکس obj[key] = value.
  • __delitem__: حذف با ایندکس.
  • __iter__: برای حلقه for.

مثال ۱: دنباله سفارشی (Custom Sequence)

ساخت کلاسی که توان‌های ۲ را در یک بازه ایندکس شده برمی‌گرداند بدون اینکه آن‌ها را در حافظه ذخیره کند.

Python

مثال ۲: ساخت دیکشنری با مقدار پیش‌فرض (__missing__)

متد __missing__ فقط مخصوص زیرکلاس‌های dict است و زمانی اجرا می‌شود که کلید وجود نداشته باشد.

Python

۴. شیء‌های قابل فراخوانی (Callables)

با پیاده‌سازی __call__، نمونه‌های کلاس شما می‌توانند مثل یک تابع با پرانتز () اجرا شوند. این روش برای نگهداری وضعیت (State) در توابع بسیار تمیزتر از استفاده از global یا Closure های پیچیده است.

مثال ۱: کش کردن نتایج (Memoization)

Python

مثال ۲: کلاس به عنوان دکوریتور

از آنجا که دکوریتورها خودشان تابع هستند، می‌توان از کلاس‌های دارای __call__ برای نوشتن دکوریتورهای پیشرفته استفاده کرد.

python
# Static: Structure of a Class-based Decorator
class Logger:
    def __init__(self, func):
        self.func = func
    
    def __call__(self, *args, **kwargs):
        print(f"Calling {self.func.__name__}")
        return self.func(*args, **kwargs)

@Logger
def target():
    pass

۵. مدیریت کانتکست (Context Managers)

دستور with به متدهای __enter__ و __exit__ متکی است. این بهترین روش برای مدیریت منابع (فایل، شبکه، دیتابیس) است تا مطمئن شویم حتی در صورت بروز خطا، منابع آزاد می‌شوند.

مثال ۱: اندازه‌گیری زمان اجرا

Python

مثال ۲: مدیریت خطا و سرکوب آن

Python