سطح مقدماتی (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__ است.
مثال ۲: الگوی Singleton با استفاده از __new__
از __new__ زمانی استفاده میشود که بخواهیم کنترل کنیم آیا شیء جدیدی ساخته شود یا خیر (مثلاً فقط یک نمونه از کلاس در کل برنامه وجود داشته باشد).
مثال ۳: مدیریت حذف با __del__
هشدار: به زمان اجرای __del__ اعتماد کامل نکنید، اما برای لاگگیری یا بستن منابع خارجی مفید است.
# 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__
پایتون دو روش برای تبدیل شیء به رشته دارد. تفاوت این دو بسیار حیاتی است:
__str__: هدف آن خوانایی برای کاربر نهایی است. وقتیprint(obj)یاstr(obj)را صدا میزنید.__repr__: هدف آن رفع ابهام برای برنامهنویس است. وقتی نام متغیر را در کنسول تایپ میکنید یاrepr(obj)را صدا میزنید. قانون طلایی: خروجیreprباید در صورت امکان کدی باشد که شیء را دوباره بسازد.
مثال ۱: تفاوت نمایشی
مثال ۲: دیباگ راحتتر با __repr__
اگر __repr__ را تعریف نکنید، خروجی پیشفرض چیزی شبیه <__main__.Obj object at 0x7f...> است که هیچ اطلاعات مفیدی ندارد.
۳. عملگرهای مقایسهای (Comparison Operators)
برای اینکه اشیاء شما قابل مرتبسازی (Sort) یا استفاده در شرطها باشند، باید این متدها را پیادهسازی کنید:
__eq__ (==), __ne__ (!=), __lt__ (<), __gt__ (>), __le__ (<=), __ge__ (>=).
مثال ۱: مقایسه موجودی حسابها
مثال ۲: مرتبسازی لیست اشیاء
وقتی __lt__ (کوچکتر از) را تعریف کنید، پایتون میتواند لیستی از اشیاء شما را sort کند.
سطح پیشرفته (Professional Level)
در این سطح به سراغ تغییر رفتار عملگرهای ریاضی، مدیریت دسترسی پویا به ویژگیها (Reflection)، ساخت کانتینرهای سفارشی و مدیریت حافظه میرویم.
۱. سربارگذاری عملگرهای ریاضی (Operator Overloading)
برای پشتیبانی کامل از ریاضیات، سه دسته متد وجود دارد:
- Normal (
__add__): وقتی عملوند چپ، کلاس ما باشد (obj + 1). - Reverse (
__radd__): وقتی عملوند چپ کلاس ما را نشناسد و عملوند راست کلاس ما باشد (1 + obj). - In-Place (
__iadd__): برای عملیات انتسابی (obj += 1).
مثال ۱: پیادهسازی بردار ریاضی (Vector)
این مثال هر سه نوع عملگر را نشان میدهد.
مثال ۲: ضرب اسکالر سفارشی
۲. مدیریت دسترسی به ویژگیها (Attribute Access Hooks)
پایتون به شما اجازه میدهد فرآیند خواندن یا نوشتن obj.attribute را کاملاً تغییر دهید. این تکنیک در ساخت ORMها و سیستمهای RPC بسیار کاربرد دارد.
__getattr__(self, name): فقط زمانی صدا زده میشود که ویژگی پیدا نشود (Fallback).__getattribute__(self, name): همیشه صدا زده میشود (مواظب حلقههای بینهایت باشید!).__setattr__(self, name, value): هنگام مقداردهی ویژگی.__delattr__(self, name): هنگام حذف ویژگی.
مثال ۱: دیکشنری هوشمند (Dot Notation)
تبدیل دیکشنری به شکلی که بتوان با نقطه به کلیدها دسترسی داشت.
مثال ۲: اعتبارسنجی هنگام مقداردهی (__setattr__)
جلوگیری از تغییر ویژگیهای خاص یا اعتبارسنجی دادهها.
۳. پروتکلهای کانتینر (Container Protocols)
برای ساختن کلاسی که مثل list یا dict رفتار کند، باید متدهای زیر را پیادهسازی کنید:
__len__: طول شیء.__getitem__: دسترسی با ایندکسobj[key].__setitem__: مقداردهی با ایندکسobj[key] = value.__delitem__: حذف با ایندکس.__iter__: برای حلقهfor.
مثال ۱: دنباله سفارشی (Custom Sequence)
ساخت کلاسی که توانهای ۲ را در یک بازه ایندکس شده برمیگرداند بدون اینکه آنها را در حافظه ذخیره کند.
مثال ۲: ساخت دیکشنری با مقدار پیشفرض (__missing__)
متد __missing__ فقط مخصوص زیرکلاسهای dict است و زمانی اجرا میشود که کلید وجود نداشته باشد.
۴. شیءهای قابل فراخوانی (Callables)
با پیادهسازی __call__، نمونههای کلاس شما میتوانند مثل یک تابع با پرانتز () اجرا شوند. این روش برای نگهداری وضعیت (State) در توابع بسیار تمیزتر از استفاده از global یا Closure های پیچیده است.
مثال ۱: کش کردن نتایج (Memoization)
مثال ۲: کلاس به عنوان دکوریتور
از آنجا که دکوریتورها خودشان تابع هستند، میتوان از کلاسهای دارای __call__ برای نوشتن دکوریتورهای پیشرفته استفاده کرد.
# 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__ متکی است. این بهترین روش برای مدیریت منابع (فایل، شبکه، دیتابیس) است تا مطمئن شویم حتی در صورت بروز خطا، منابع آزاد میشوند.