سطح مقدماتی (Beginner Level)
برای درک مفهوم کلوژر (Closure)، ابتدا باید با نحوه کارکرد توابع تودرتو (Nested Functions) و قلمرو متغیرها (Scope) آشنا باشیم. کلوژر یکی از مفاهیم قدرتمند پایتون است که به یک تابع اجازه میدهد تا به متغیرهای محیطی که در آن تعریف شده است، دسترسی داشته باشد، حتی اگر اجرای تابع بیرونی به پایان رسیده باشد.
۱. تابع تودرتو (Nested Function) چیست؟
در پایتون میتوانیم یک تابع را درون یک تابع دیگر تعریف کنیم. تابع داخلی به متغیرهای تابع بیرونی دسترسی دارد.
# تعریف تابع بدون اجرا (Static)
def outer_function(message):
# این متغیر متعلق به تابع بیرونی است
text = message
def inner_function():
# تابع داخلی به متغیر text دسترسی دارد
print(text)
# تابع بیرونی، تابع داخلی را صدا میزند
inner_function()
۲. تعریف کلوژر (Closure)
کلوژر زمانی شکل میگیرد که:
- یک تابع تودرتو داشته باشیم.
- تابع داخلی به متغیرهای تابع بیرونی (Non-local) ارجاع دهد.
- تابع بیرونی، خودِ تابع داخلی را (بدون پرانتز) برگرداند (Return کند).
در این حالت، تابع داخلی حتی پس از حذف شدن تابع بیرونی از حافظه، مقادیر متغیرهای آن را در خود "حبس" یا "ذخیره" میکند.
مثال اول: ساخت یک کلوژر ساده
در این مثال، تابع multiplier یک عدد را میگیرد و یک تابع جدید تولید میکند که همیشه ورودی خود را در آن عدد ضرب میکند.
مثال دوم: ذخیره وضعیت (State Retention)
کلوژرها میتوانند جایگزین سادهای برای کلاسها باشند زمانی که میخواهید مقداری داده را در حافظه نگه دارید.
سطح پیشرفته (Professional Level)
در سطح حرفهای، باید بدانیم پایتون چگونه این متغیرها را مدیریت میکند و چه زمانی استفاده از کلوژر بهینهتر از کلاس یا روشهای دیگر است. همچنین مشکلات رایج مثل Late Binding را بررسی میکنیم.
۱. کالبدشکافی کلوژر (__closure__ و cell objects)
در پایتون، توابع آبجکت هستند. وقتی یک تابع کلوژر ایجاد میکند، متغیرهای آزاد (Free Variables) در ویژگی خاصی به نام __closure__ ذخیره میشوند. این ویژگی حاوی تاپلی از سلولها (Cells) است.
۲. مشکل Late Binding (دام حلقهها)
یکی از خطاهای رایج هنگام استفاده از کلوژرها یا توابع لامبدا در حلقهها، پدیده Late Binding است. پایتون متغیرها را بر اساس نام جستجو میکند، نه بر اساس مقدار لحظهای آنها در زمان تعریف.
کد مشکلدار:
# Static: This code has a logical bug
def create_multipliers():
multipliers = []
for i in range(3):
# تابع داخلی به i ارجاع میدهد، اما i تغییر میکند
def func(x):
return x * i
multipliers.append(func)
return multipliers
# وقتی توابع را اجرا کنیم، همه آنها آخرین مقدار i (یعنی 2) را میبینند!
# funcs = create_multipliers()
# funcs[0](10) -> 20 (انتظار 0 بود)
راهحل حرفهای (استفاده از Default Argument):
برای حل این مشکل، باید مقدار لحظهای را به عنوان یک آرگومان پیشفرض به تابع پاس دهیم تا در زمان تعریف (Definition Time) ارزیابی شود.
۳. کلوژر در مقابل کلاس (Performance & Clean Code)
استفاده از کلوژر معمولاً سریعتر و سبکتر از تعریف یک کلاس کامل است، به شرطی که فقط یک متد اصلی داشته باشید.

نکات کلیدی فنی
- حافظه: متغیرهای داخل کلوژر تا زمانی که خود تابع
innerزنده است، توسط Garbage Collector پاک نمیشوند. - تغییر مقادیر: اگر متغیر
immutableباشد (مثل عدد یا رشته)، برای تغییر آن در تابع داخلی حتماً باید ازnonlocalاستفاده کنید. اگرmutableباشد (مثل لیست)، نیازی بهnonlocalبرای تغییر محتویات لیست نیست (اما برای انتساب مجدد لیست نیاز است).