النسخ السطحية والعميقة: كيفية نسخ البيانات المعقدة بشكل صحيح في Python

في الفصل السابق، تعلمت أن copy = original لا تنسخ البيانات، بل تنسخ المرجع فقط. الآن ستتعلم كيفية نسخ البيانات بشكل صحيح: متى تستخدم النسخة السطحية (Shallow Copy)، ومتى تحتاج إلى النسخة العميقة (Deep Copy)؟ هذا الفهم أساسي للعمل مع القوائم والقواامس والكائنات المعقدة.

1. مراجعة سريعة: النسخ البسيط vs النسخ الحقيقي

تذكر أن copy = original ينسخ المرجع (Reference)، وليس البيانات الفعلية. هذا يعني أن كلا المتغيرين يشيران إلى نفس الكائن في الذاكرة:

reference_copy.py
# النسخ البسيط (Reference Copy) - خطر!
original = [1, 2, 3]
copy = original

print(f"id(original): {id(original)}")
print(f"id(copy): {id(copy)}")
print(f"هل هما نفس الكائن؟ {original is copy}")  # True!

# تعديل copy يؤثر على original
copy.append(4)
print(f"original: {original}")  # [1, 2, 3, 4] (تغيرت!)
النتيجة
id(original): 140234567890123 id(copy): 140234567890123 هل هما نفس الكائن؟ True original: [1, 2, 3, 4]

الحل هو استخدام Shallow Copy أو Deep Copy. الفرق يكمن في عمق النسخ:

2. النسخة السطحية (Shallow Copy)

Shallow Copy تنسخ الكائن الأساسي فقط، لكنها تحافظ على نفس المراجع للعناصر الداخلية. بمعنى آخر: إذا كانت قائمتك تحتوي على قوائم أخرى (nested lists)، فإن النسخة السطحية ستشير إلى نفس القوائس الداخلية.

مثال 1: Shallow Copy مع قائمة بسيطة
shallow_copy_simple.py
# Shallow Copy مع قائمة بسيطة
original = [1, 2, 3]
shallow = original.copy()  # أو list(original)

print(f"id(original): {id(original)}")
print(f"id(shallow): {id(shallow)}")
print(f"هل هما نفس الكائن؟ {original is shallow}")  # False

# تعديل shallow لا يؤثر على original
shallow.append(4)
print(f"original: {original}")  # [1, 2, 3]
print(f"shallow: {shallow}")    # [1, 2, 3, 4]
النتيجة
id(original): 140234567890123 id(shallow): 140234567890456 هل هما نفس الكائن؟ False original: [1, 2, 3] shallow: [1, 2, 3, 4]

ممتاز! هنا Shallow Copy عملت بشكل صحيح لأن عناصر القائمة أرقام (أنواع بسيطة). لكن ماذا يحدث عندما تحتوي القائمة على قوائس أخرى؟

مثال 2: الخطر المخفي - Shallow Copy مع Nested Lists
shallow_copy_nested.py
# Shallow Copy مع قائمة متعددة الأبعاد
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

shallow = matrix.copy()

print(f"matrix is shallow: {matrix is shallow}")        # False (الحاويات مختلفة)
print(f"matrix[0] is shallow[0]: {matrix[0] is shallow[0]}")  # True! (نفس الداخل)

# تعديل العنصر الداخلي
shallow[0][0] = 999

print(f"matrix: {matrix}")    # [[999, 2, 3], [4, 5, 6], [7, 8, 9]] (تغيرت!)
print(f"shallow: {shallow}")  # [[999, 2, 3], [4, 5, 6], [7, 8, 9]]
النتيجة
matrix is shallow: False matrix[0] is shallow[0]: True matrix: [[999, 2, 3], [4, 5, 6], [7, 8, 9]] shallow: [[999, 2, 3], [4, 5, 6], [7, 8, 9]]

هنا المشكلة! Shallow Copy نسخت حاوية القائمة الخارجية فقط، لكن العناصر الداخلية (القوائس) تشير إلى نفس الموقع. لذلك عندما غيرنا shallow[0][0]، تأثرت matrix[0][0] أيضاً.

مثال 3: Shallow Copy مع Dictionaries
shallow_copy_dict.py
# Shallow Copy مع dictionary يحتوي على قوائم
person = {
    "name": "أحمد",
    "grades": [90, 85, 88]
}

person_copy = person.copy()

# تعديل القيمة الأساسية (string) - آمن
person_copy["name"] = "محمد"
print(f"person['name']: {person['name']}")  # أحمد (لم تتغير)

# تعديل قائمة داخل dictionary - خطر!
person_copy["grades"].append(95)
print(f"person['grades']: {person['grades']}")  # [90, 85, 88, 95] (تغيرت!)
النتيجة
person['name']: أحمد person['grades']: [90, 85, 88, 95]

الخلاصة عن Shallow Copy: تستخدم عندما تكون البيانات بسيطة. لكن إذا كانت البيانات متعددة المستويات (قوائس داخل قوائس، قواميس تحتوي على قوائس، إلخ)، فستحتاج إلى Deep Copy.

3. النسخة العميقة (Deep Copy)

Deep Copy تنسخ الكائن بالكامل، بما فيه جميع العناصر الداخلية. هذا يعني أن كل شيء سيكون مستقلاً تماماً، وتعديل النسخة لن يؤثر على الأصلية.

لاستخدام Deep Copy، تحتاج إلى استيراد وحدة copy:

deep_copy_import.py
from copy import deepcopy

# Deep Copy مع قائمة متعددة الأبعاد
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

deep = deepcopy(matrix)

# تعديل العنصر الداخلي
deep[0][0] = 999

print(f"matrix: {matrix}")    # [[1, 2, 3], [4, 5, 6], [7, 8, 9]] (لم تتغير!)
print(f"deep: {deep}")        # [[999, 2, 3], [4, 5, 6], [7, 8, 9]]
النتيجة
matrix: [[1, 2, 3], [4, 5, 6], [7, 8, 9]] deep: [[999, 2, 3], [4, 5, 6], [7, 8, 9]]

رائع! Deep Copy نسخت كل شيء بشكل مستقل. الآن يمكنك تعديل النسخة بدون التأثير على الأصلية.

مثال 1: Deep Copy مع Dictionary معقد
deep_copy_complex_dict.py
from copy import deepcopy

# Dictionary معقد
student = {
    "name": "أحمد",
    "age": 20,
    "grades": [90, 85, 88],
    "courses": {
        "math": {"score": 92, "teacher": "محمد"},
        "science": {"score": 88, "teacher": "فاطمة"}
    }
}

# Deep Copy
student_copy = deepcopy(student)

# تعديل البيانات المتعددة المستويات
student_copy["name"] = "علي"
student_copy["grades"].append(95)
student_copy["courses"]["math"]["score"] = 100

print("البيانات الأصلية:")
print(f"الاسم: {student['name']}")  # أحمد
print(f"الدرجات: {student['grades']}")  # [90, 85, 88]
print(f"درجة الرياضيات: {student['courses']['math']['score']}")  # 92

print("\nالنسخة المعدلة:")
print(f"الاسم: {student_copy['name']}")  # علي
print(f"الدرجات: {student_copy['grades']}")  # [90, 85, 88, 95]
print(f"درجة الرياضيات: {student_copy['courses']['math']['score']}")  # 100
النتيجة
البيانات الأصلية: الاسم: أحمد الدرجات: [90, 85, 88] درجة الرياضيات: 92 النسخة المعدلة: الاسم: علي الدرجات: [90, 85, 88, 95] درجة الرياضيات: 100

ممتاز! Deep Copy نسخت كل شيء بشكل مستقل، حتى الكائنات المتعددة المستويات.

مثال 2: Deep Copy مع Nested Lists
deep_copy_nested_list.py
from copy import deepcopy

# قائمة متعددة الأبعاد جداً
data = [
    [1, 2, [3, 4, 5]],
    [6, 7, [8, 9, 10]],
    [11, 12, [13, 14, 15]]
]

deep = deepcopy(data)

# تعديل العناصر المتعمقة
deep[0][2][0] = 999
deep[1][2].append(100)

print("البيانات الأصلية:")
print(data)

print("\nالنسخة المعدلة:")
print(deep)
النتيجة
البيانات الأصلية: [[1, 2, [3, 4, 5]], [6, 7, [8, 9, 10]], [11, 12, [13, 14, 15]]] النسخة المعدلة: [[1, 2, [3, 4, 5]], [6, 7, [8, 9, 10, 100]], [11, 12, [13, 14, 15]]]

4. جدول مقارنة شامل

النوع الاستخدام الأداء الاستقلالية
copy = original نسخ المرجع فقط سريع جداً ❌ مشترك تماماً
.copy() أو list() Shallow Copy سريع ⚠️ الحاوية مستقلة، العناصر الداخلية مشتركة
deepcopy() Deep Copy أبطأ ✅ مستقل تماماً

متى تستخدم كل واحد؟

  • Reference Copy: عندما تريد أن يشير متغيرين إلى نفس الكائن (نادراً ما تريد هذا)
  • Shallow Copy: عندما تكون البيانات بسيطة (قوائم من أرقام، قواميس بقيم بسيطة)
  • Deep Copy: عندما تكون البيانات معقدة (قوائس متعددة الأبعاد، قواميس متعددة المستويات، كائنات معقدة)

5. طرق مختلفة لعمل Shallow Copy

هناك عدة طرق لعمل Shallow Copy:

1. باستخدام .copy()
shallow_method_1.py
# List
list1 = [1, 2, 3]
list2 = list1.copy()

# Dictionary
dict1 = {"a": 1, "b": 2}
dict2 = dict1.copy()
2. باستخدام Constructor
shallow_method_2.py
# List
list1 = [1, 2, 3]
list2 = list(list1)

# Dictionary
dict1 = {"a": 1, "b": 2}
dict2 = dict(dict1)
3. باستخدام Slicing (للقوائم فقط)
shallow_method_3.py
# List
list1 = [1, 2, 3]
list2 = list1[:]  # Slicing يعمل كـ Shallow Copy
4. باستخدام copy module (للـ Shallow Copy أيضاً)
shallow_method_4.py
from copy import copy

list1 = [1, 2, 3]
list2 = copy(list1)

dict1 = {"a": 1, "b": 2}
dict2 = copy(dict1)

6. أمثلة عملية واقعية

مثال 1: إدارة قائمة الطلاب
students_management.py
from copy import deepcopy

# قائمة الطلاب الأصلية
original_students = [
    {"id": 1, "name": "أحمد", "grades": [90, 85, 88]},
    {"id": 2, "name": "فاطمة", "grades": [92, 89, 91]},
    {"id": 3, "name": "محمد", "grades": [88, 87, 90]}
]

# إنشاء نسخة لتعديلها (الحل الخاطئ)
students_wrong = original_students.copy()
students_wrong[0]["grades"].append(100)

# النتيجة: تأثرت البيانات الأصلية!
print("الحل الخاطئ - تأثرت البيانات الأصلية:")
print(f"original_students[0]['grades']: {original_students[0]['grades']}")
# النتيجة: [90, 85, 88, 100]

# الحل الصحيح
original_students = [
    {"id": 1, "name": "أحمد", "grades": [90, 85, 88]},
    {"id": 2, "name": "فاطمة", "grades": [92, 89, 91]},
    {"id": 3, "name": "محمد", "grades": [88, 87, 90]}
]

students_correct = deepcopy(original_students)
students_correct[0]["grades"].append(100)

print("\nالحل الصحيح - البيانات الأصلية آمنة:")
print(f"original_students[0]['grades']: {original_students[0]['grades']}")
# النتيجة: [90, 85, 88]
print(f"students_correct[0]['grades']: {students_correct[0]['grades']}")
# النتيجة: [90, 85, 88, 100]
النتيجة
الحل الخاطئ - تأثرت البيانات الأصلية: original_students[0]['grades']: [90, 85, 88, 100] الحل الصحيح - البيانات الأصلية آمنة: original_students[0]['grades']: [90, 85, 88] students_correct[0]['grades']: [90, 85, 88, 100]
مثال 2: تعديل البيانات في حلقة
loop_modification.py
from copy import deepcopy

# قائمة أصلية
products = [
    {"name": "كتاب", "price": 50, "quantity": 10},
    {"name": "قلم", "price": 5, "quantity": 20},
    {"name": "دفتر", "price": 15, "quantity": 15}
]

# نسخة عميقة للعمل عليها
products_backup = deepcopy(products)

# تطبيق خصم على الأسعار
for product in products_backup:
    product["price"] = product["price"] * 0.9  # خصم 10%

print("الأسعار الأصلية:")
for p in products:
    print(f"{p['name']}: {p['price']}")

print("\nالأسعار بعد الخصم:")
for p in products_backup:
    print(f"{p['name']}: {p['price']}")
النتيجة
الأسعار الأصلية: كتاب: 50 قلم: 5 دفتر: 15 الأسعار بعد الخصم: كتاب: 45.0 قلم: 4.5 دفتر: 13.5
مثال 3: نسخ قاموس معقد
complex_dict_copy.py
from copy import deepcopy

# إعدادات التطبيق (dictionary معقد)
config = {
    "database": {
        "host": "localhost",
        "port": 5432,
        "credentials": ["admin", "password123"]
    },
    "api": {
        "endpoints": ["users", "products", "orders"],
        "timeout": 30
    }
}

# محاولة نسخ الإعدادات (الحل الخاطئ)
config_dev = config.copy()
config_dev["database"]["host"] = "dev-server"
config_dev["database"]["credentials"][0] = "dev_admin"

print("الإعدادات الأصلية (تأثرت!):")
print(f"Host: {config['database']['host']}")
print(f"User: {config['database']['credentials'][0]}")

# الحل الصحيح
config = {
    "database": {
        "host": "localhost",
        "port": 5432,
        "credentials": ["admin", "password123"]
    },
    "api": {
        "endpoints": ["users", "products", "orders"],
        "timeout": 30
    }
}

config_dev = deepcopy(config)
config_dev["database"]["host"] = "dev-server"
config_dev["database"]["credentials"][0] = "dev_admin"

print("\nالإعدادات الأصلية (آمنة الآن):")
print(f"Host: {config['database']['host']}")
print(f"User: {config['database']['credentials'][0]}")

print("\nإعدادات التطوير:")
print(f"Host: {config_dev['database']['host']}")
print(f"User: {config_dev['database']['credentials'][0]}")
النتيجة
الإعدادات الأصلية (تأثرت!): Host: dev-server User: dev_admin الإعدادات الأصلية (آمنة الآن): Host: localhost User: admin إعدادات التطوير: Host: dev-server User: dev_admin

7. الأخطاء الشائعة والحلول

الخطأ 1: نسيان أن Shallow Copy قد لا تكون كافية
error_shallow_not_enough.py
# الخطأ: استخدام Shallow Copy مع بيانات متعددة المستويات
teams = [
    {"name": "فريق أ", "members": ["أحمد", "فاطمة"]},
    {"name": "فريق ب", "members": ["محمد", "علي"]}
]

teams_copy = teams.copy()  # Shallow Copy - غير كافي!
teams_copy[0]["members"].append("سارة")

# المشكلة:
print(teams[0]["members"])  # ['أحمد', 'فاطمة', 'سارة'] (تغيرت!)

# الحل:
from copy import deepcopy
teams_copy = deepcopy(teams)  # Deep Copy - الحل الصحيح
teams_copy[0]["members"].append("سارة")

print(teams[0]["members"])  # ['أحمد', 'فاطمة'] (آمن!)
الخطأ 2: الخلط بين reference copy و shallow copy
error_reference_vs_shallow.py
# الخطأ الشائع: عدم التمييز بين النسخ
data = [1, 2, 3]

# Reference copy (خطر!)
ref = data

# Shallow copy (أفضل قليلاً)
shallow = data.copy()

# Deep copy (الأفضل للبيانات المعقدة)
from copy import deepcopy
deep = deepcopy(data)

# القاعدة:
# - استخدم .copy() للبيانات البسيطة
# - استخدم deepcopy() للبيانات المعقدة
# - تجنب reference copy (=) إلا إذا كنت تريد ذلك بقصد
الخطأ 3: نسيان استيراد deepcopy
error_import_deepcopy.py
# الخطأ
# deep = deepcopy(data)  # NameError: name 'deepcopy' is not defined

# الحل
from copy import deepcopy

data = [[1, 2, 3], [4, 5, 6]]
deep = deepcopy(data)  # الآن يعمل بشكل صحيح

8. نصائح مهمة وأفضل الممارسات

  • افهم البيانات أولاً: قبل النسخ، اسأل نفسك: "هل بياناتي متعددة المستويات أم بسيطة؟"
  • استخدم .copy() كخط أول: للبيانات البسيطة، هذا كافٍ وأسرع
  • استخدم deepcopy() للأمان: إذا كنت غير متأكد، استخدم deepcopy لتكون آمناً
  • تجنب reference copy (=): إلا إذا كنت تريد متغيرين يشيران إلى نفس الكائن
  • اختبر النسخ في الوثائق: في البرامج الكبيرة، وثّق نوع النسخ المستخدم
  • احذر من الأداء: deepcopy قد تكون بطيئة مع البيانات الضخمة
  • استخدم القائمات البرمجية (List Comprehensions): للنسخ البسيطة من البيانات البسيطة
مثال: استخدام List Comprehension للنسخ
list_comprehension_copy.py
# طريقة بسيطة: استخدام list comprehension
original = [1, 2, 3, 4, 5]
copy = [x for x in original]  # نسخة مستقلة

copy.append(6)
print(f"original: {original}")  # [1, 2, 3, 4, 5]
print(f"copy: {copy}")          # [1, 2, 3, 4, 5, 6]

9. مثال عملي شامل: نظام إدارة الموارد

resource_management.py
from copy import deepcopy

class ResourceManager:
    def __init__(self):
        self.resources = {
            "servers": [
                {"name": "server1", "cpu": 4, "memory": 8},
                {"name": "server2", "cpu": 8, "memory": 16}
            ],
            "storage": [
                {"name": "storage1", "capacity": 1000}
            ]
        }
    
    def create_backup(self):
        """إنشاء نسخة احتياطية كاملة"""
        return deepcopy(self.resources)
    
    def get_snapshot(self):
        """الحصول على لقطة سريعة (shallow copy)"""
        return self.resources.copy()
    
    def modify_resource(self, resource_type, index, key, value):
        """تعديل موقع معين"""
        if resource_type in self.resources:
            if index < len(self.resources[resource_type]):
                self.resources[resource_type][index][key] = value
                return True
        return False
    
    def test_modification(self):
        """اختبار التعديلات بدون التأثير على الأصلية"""
        backup = self.create_backup()
        backup["servers"][0]["cpu"] = 16
        
        # المقارنة
        print("الموارد الأصلية:")
        print(f"CPU للخادم الأول: {self.resources['servers'][0]['cpu']}")
        
        print("\nالنسخة الاحتياطية (معدلة):")
        print(f"CPU للخادم الأول: {backup['servers'][0]['cpu']}")

# الاستخدام
manager = ResourceManager()
manager.test_modification()
النتيجة
الموارد الأصلية: CPU للخادم الأول: 4 النسخة الاحتياطية (معدلة): CPU للخادم الأول: 16

10. تمرين عملي

قم بكتابة برنامج يقوم بالمهام التالية:

  1. أنشئ قاموس متعمق (nested dictionary) يحتوي على معلومات طالب بما فيها القسم والمواد والدرجات
  2. قم بإنشاء نسخة shallow copy باستخدام .copy() واعدل درجة ما
  3. استعد البيانات الأصلية وقم بإنشاء deep copy باستخدام deepcopy() واعدل نفس الدرجة
  4. قارن النتائج لترى الفرق
  5. استخدم id() و is للتحقق من الاختلافات

تفكر متقدم: اكتب دالة تقبل كائن معقد وتحدد تلقائياً ما إذا كانت Shallow Copy أم Deep Copy ضرورية

الخلاصة والانتقال إلى الفصل التالي

لقد تعلمت اليوم:

  • الفرق بين Reference Copy و Shallow Copy و Deep Copy
  • متى تستخدم .copy() ومتى تستخدم deepcopy()
  • الأخطاء الشائعة عند نسخ البيانات المعقدة
  • أمثلة عملية واقعية لاستخدام النسخ الصحيح
  • كيفية اختبار وتشخيص مشاكل النسخ
  • أفضل الممارسات عند التعامل مع البيانات المعقدة

في الفصل التالي، ستتعلم عن البرمجة الموجهة للكائنات (OOP)، حيث ستستخدم هذه المعرفة عندما تعمل مع كائنات معقدة. الفهم الجيد للنسخ سيساعدك على تجنب أخطاء صعبة التتبع في البرامج الكبيرة.

المحرر الذكي

اكتب الكود وشاهد النتيجة فوراً

جرب الآن مجاناً
قناة ديف عربي

تابع أحدث الدروس والتحديثات مباشرة على واتساب

انضم الآن