کپی و Deepcopy در پایتون: درک عمیق

کپی و Deepcopy در پایتون: درک عمیق

در پایتون، تخصیص متغیرها به روشی که ممکن است در زبان‌های دیگر انتظار داشته باشید، عمل نمی‌کند. به جای کپی کردن مقدار، پایتون معمولاً یک ارجاع (reference) به شیء اصلی ایجاد می‌کند. این موضوع می‌تواند منجر به رفتارهای غیرمنتظره‌ای شود، به خصوص زمانی که با اشیاء قابل تغییر (mutable objects) مانند لیست‌ها و دیکشنری‌ها سر و کار دارید. برای جلوگیری از این مشکلات، پایتون دو روش برای کپی کردن اشیاء ارائه می‌دهد: copy() (کپی سطحی) و deepcopy() (کپی عمیق). این مقاله به بررسی دقیق این دو روش، تفاوت‌های آن‌ها و نحوه استفاده صحیح از آن‌ها می‌پردازد.

تخصیص متغیرها و ارجاع‌ها در پایتون

قبل از پرداختن به کپی کردن، مهم است که درک کنیم وقتی یک متغیر را به متغیر دیگری تخصیص می‌دهیم، چه اتفاقی می‌افتد. در پایتون، تخصیص متغیرها به معنای ایجاد یک ارجاع جدید به همان شیء در حافظه است، نه ایجاد یک کپی از آن. به عبارت دیگر، هر دو متغیر به یک مکان در حافظه اشاره می‌کنند.


a = [1, 2, 3]
b = a
b.append(4)
print(a)  # خروجی: [1, 2, 3, 4]
print(b)  # خروجی: [1, 2, 3, 4]

در این مثال، b = a یک کپی از لیست a ایجاد نمی‌کند. بلکه b به همان لیست a در حافظه اشاره می‌کند. بنابراین، هر تغییری که در b اعمال شود، بر روی a نیز تأثیر می‌گذارد و بالعکس.

کپی سطحی (Shallow Copy) با استفاده از copy()

کپی سطحی با استفاده از متد copy() یا تابع copy.copy() ایجاد می‌شود. این روش یک شیء جدید ایجاد می‌کند، اما فقط ارجاع‌هایی به اشیاء موجود در شیء اصلی را کپی می‌کند. به عبارت دیگر، اگر شیء اصلی شامل اشیاء قابل تغییر دیگری باشد (مانند لیست‌های تو در تو)، کپی سطحی فقط ارجاعی به این اشیاء تو در تو را کپی می‌کند، نه خود اشیاء را.


import copy

a = [1, 2, [3, 4]]
b = a.copy()  # یا b = copy.copy(a)

b.append(5)
print(a)  # خروجی: [1, 2, [3, 4]]
print(b)  # خروجی: [1, 2, [3, 4], 5]

b[2].append(5)
print(a)  # خروجی: [1, 2, [3, 4, 5]]
print(b)  # خروجی: [1, 2, [3, 4, 5], 5]

در این مثال، b یک لیست جدید است، اما لیست تو در تو [3, 4] همچنان به همان لیست در حافظه اشاره می‌کند. بنابراین، تغییر در لیست تو در تو در b بر روی a نیز تأثیر می‌گذارد.

کپی عمیق (Deep Copy) با استفاده از deepcopy()

کپی عمیق با استفاده از تابع copy.deepcopy() ایجاد می‌شود. این روش یک شیء جدید ایجاد می‌کند و به صورت بازگشتی تمام اشیاء موجود در شیء اصلی را کپی می‌کند. به عبارت دیگر، کپی عمیق یک کپی مستقل از شیء اصلی ایجاد می‌کند، به طوری که هیچ تغییری در کپی بر روی شیء اصلی تأثیر نمی‌گذارد و بالعکس.


import copy

a = [1, 2, [3, 4]]
b = copy.deepcopy(a)

b.append(5)
print(a)  # خروجی: [1, 2, [3, 4]]
print(b)  # خروجی: [1, 2, [3, 4], 5]

b[2].append(5)
print(a)  # خروجی: [1, 2, [3, 4]]
print(b)  # خروجی: [1, 2, [3, 4, 5], 5]

در این مثال، b یک لیست جدید است و لیست تو در تو [3, 4] نیز یک لیست جدید است که کپی شده از لیست تو در تو در a است. بنابراین، تغییر در لیست تو در تو در b بر روی a تأثیر نمی‌گذارد.

چه زمانی از copy() و چه زمانی از deepcopy() استفاده کنیم؟

انتخاب بین copy() و deepcopy() بستگی به نیازهای خاص شما دارد.

  • copy(): اگر شیء شما شامل اشیاء قابل تغییر دیگری نیست، یا اگر می‌خواهید تغییرات در کپی بر روی شیء اصلی نیز تأثیر بگذارد، می‌توانید از copy() استفاده کنید. این روش سریع‌تر و کم‌هزینه‌تر از deepcopy() است.
  • deepcopy(): اگر شیء شما شامل اشیاء قابل تغییر دیگری است و می‌خواهید یک کپی کاملاً مستقل از شیء اصلی ایجاد کنید، باید از deepcopy() استفاده کنید. این روش کندتر و پرهزینه‌تر از copy() است، اما از بروز رفتارهای غیرمنتظره جلوگیری می‌کند.

موارد استفاده رایج

در اینجا چند مورد استفاده رایج از copy() و deepcopy() آورده شده است:

  • کپی کردن لیست‌ها و دیکشنری‌ها: برای ایجاد یک کپی از یک لیست یا دیکشنری بدون تغییر شیء اصلی.
  • کپی کردن اشیاء سفارشی: برای ایجاد یک کپی از یک شیء سفارشی که شامل ویژگی‌های قابل تغییر است.
  • ذخیره حالت یک شیء: برای ذخیره حالت یک شیء قبل از اعمال تغییرات، به طوری که بتوانید در صورت نیاز به حالت قبلی بازگردید.
  • ایجاد نسخه‌های متعدد از یک شیء: برای ایجاد نسخه‌های متعدد از یک شیء که هر کدام می‌توانند به طور مستقل تغییر کنند.

نکات مهم

  • copy() و deepcopy() فقط برای اشیاء قابل کپی (copyable) کار می‌کنند. برخی از اشیاء، مانند فایل‌ها و سوکت‌ها، قابل کپی نیستند.
  • deepcopy() می‌تواند برای اشیاء پیچیده بسیار کند باشد. اگر با اشیاء بسیار بزرگ و پیچیده کار می‌کنید، ممکن است لازم باشد به دنبال راه حل‌های جایگزین باشید.
  • درک تفاوت بین ارجاع، کپی سطحی و کپی عمیق برای نوشتن کد پایتون صحیح و قابل اعتماد بسیار مهم است.

نتیجه‌گیری

کپی کردن اشیاء در پایتون می‌تواند یک موضوع پیچیده باشد، اما با درک تفاوت بین ارجاع، کپی سطحی و کپی عمیق، می‌توانید از بروز رفتارهای غیرمنتظره جلوگیری کنید و کد خود را قابل اعتمادتر کنید. copy() برای کپی‌های سطحی و deepcopy() برای کپی‌های عمیق استفاده می‌شوند. انتخاب بین این دو روش بستگی به نیازهای خاص شما دارد. با تمرین و تجربه، می‌توانید به طور مؤثر از این ابزارها برای مدیریت اشیاء در برنامه‌های پایتون خود استفاده کنید.

بدون دیدگاه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *