في بعض الأحيان، قد ترغب في الحصول على نسخة كاملة من كائن ما، وفي أحيان أخرى قد ترغب في أن يستخدم المراجع. انظر الاختلافات في العمل.
تقدم بايثون العديد من الأساليب الفعالة لإدارة البيانات. يعد فهم مفاهيم النسخ السطحية والعميقة أمرًا بالغ الأهمية عند العمل مع بنيات البيانات مثل القوائم المتداخلة أو القواميس أو الكائنات المخصصة.
تتيح لك النسخة السطحية والعميقة إنشاء نسخ متماثلة لهياكل البيانات، ولكنها تعمل بشكل مختلف فيما يتعلق بالبيانات المتداخلة.
باستخدام نسخة ضحلة
تعمل النسخة الضحلة عن طريق إنشاء نسخة من بنية المستوى الأعلى للكائن الأصلي. وهذا يعني أنه إذا كان الكائن الأصلي يحتوي على كائنات متداخلة، فستشير النسخة إلى نفس الكائنات المتداخلة التي يشير إليها الأصل. بمعنى آخر، يؤدي إنشاء نسخة سطحية من كائن إلى تكرار بنيته الخارجية، وليس أي كائنات متداخلة قد يحتوي عليها.
لإجراء نسخة سطحية في بايثون، يمكنك استخدام وحدة النسخ ينسخ() الوظيفة أو .ينسخ() الطريقة على الكائن
النظر في مثال على العمل مع قائمة أو قاموس في بايثون.
import copy
main_list = [29, 49, ["Q", "R"]]
shallow_copy = copy.copy(main_list)# Modify the nested list
shallow_copy[2][0] = 99
main_list[2][1] = 100
print(f"The main list: {main_list}")
print(f"The shallow copy list: {shallow_copy}")
في الكود أعلاه، main_list يحتوي المتغير على قائمة تحتوي على أعداد صحيحة وقائمة داخلية (كائن متداخل) تحتوي على أحرف. تقوم وظيفة النسخ بإنشاء نسخة من main_list الذي يخزنه الكود في متغير آخر، نسخة سطحية.
أي تغييرات تجريها على نسخة سطحية ستؤثر القائمة المتداخلة أيضًا بشكل مباشر على قائمة main_list والعكس صحيح. توضح هذه التغييرات أن القائمة المتداخلة أو الداخلية لـ نسخة سطحية هي مجرد إشارة إلى ذلك من main_list، وتطبيق التغييرات في main_list أيضاً.
وفي الوقت نفسه، أي تغييرات يتم إجراؤها على العناصر الخارجية (الأعداد الصحيحة) في أي منهما نسخة سطحية أو main_list سوف يؤثر فقط على هذا المثال. وهذه العناصر الخارجية هي قيم مستقلة في حد ذاتها، وليست مجرد مراجع.
import copy
main_list = [29, 49, ["Q", "R"]]
shallow_copy = copy.copy(main_list)# Modify the outer items
shallow_copy[0] = "M"
main_list[1] = "N"
print(f"The main list: {main_list}")
print(f"The shallow copy list: {shallow_copy}")
يوضح الناتج أن العناصر الخارجية لكلا القائمتين مستقلة عن بعضها البعض:
تنطبق نفس الفكرة عند العمل مع القواميس.
dict1 = {'ten': 10, 'twenty': 20, 'double':{'thirty': 30, 'sixty': 60}}
dict2 = dict1.copy()# Modify inner and outer elements
dict1['double']['thirty'] = 30.00
dict1['ten'] = 10.00
print(f"The main dictionary, {dict1}")
print(f"The shallow copy dictionary, {dict2}")
التغييرات التي تم إجراؤها على القاموس المتداخل لـ dict1 تؤثر على حد سواء dict1 و dict2. وفي الوقت نفسه، تغييرات على العناصر الخارجية لل dict1 تؤثر عليه فقط.
باستخدام نسخة عميقة
بدلاً من الإشارة إلى الكائنات المتداخلة في النسخة الأصلية، تقوم النسخة العميقة بإنشاء نسخة منفصلة تمامًا عن الكائن الأصلي وكائناته المتداخلة. لن يؤثر تعديل النسخة العميقة على الكائن الأصلي، والعكس صحيح؛ إنها قيم منفصلة حقًا.
لإنشاء نسخة عميقة في بايثون، استخدم ملف نسخة عميقة () وظيفة وحدة النسخ.
النظر في مثال للعمل مع القائمة.
import copy
main_list = [200, 300, ["I", "J"]]
deep_copy = copy.deepcopy(main_list)# Modify the inner and outer list
deep_copy[2][0] = "K"
main_list[0] = 500
print(f"The main list: {main_list}")
print(f"The deep copy list: {deep_copy}")
هنا، يقوم الكود بتنفيذ نسخة عميقة من main_list، وإنشاء نسخة مستقلة باسم Deep_copy.
عندما تقوم بتعديل القائمة المتداخلة أو العناصر الخارجية في ملف Deep_copy، فإن تغييراتك لا تؤثر على القائمة الأصلية، والعكس صحيح. يوضح هذا أن القائمة المتداخلة أو العناصر الخارجية غير مشتركة بين النسختين.
العمل مع الكائنات المخصصة
يمكنك إنشاء كائن مخصص بواسطة تحديد فئة بايثون وإنشاء مثيل للفئة.
فيما يلي مثال لإنشاء كائن بسيط من ملف كتاب فصل:
classBook:
def__init__(self, title, authors, price):
self.title = title
self.authors = authors
self.price = price
def__str__(self):
returnf"Book(title='{self.title}', author='{self.authors}', \
price='{self.price}')"
الآن، قم بإنشاء نسخة سطحية ونسخة عميقة لمثال على ذلك كتاب الطبقة باستخدام ينسخ وحدة.
import copy
# Create a Book object
book1 = Book("How to MakeUseOf Shallow Copy", \
["Bobby Jack", "Princewill Inyang"], 1000)# Make a shallow copy
book2 = copy.copy(book1)# Modify the original object
book1.authors.append("Yuvraj Chandra")
book1.price = 50
# Check the objects
print(book1)
print(book2)
كما ترون، النسخة الضحلة (كتاب2) هو كائن جديد، ولكنه يشير إلى نفس الكائن الداخلي (قائمة المؤلفين) مثل الكائن الأصلي (كتاب 1). ومن ثم، فإن التغيير في مؤلفي الكائن الأصلي يؤثر على كلا الحالتين (الكتاب 1 والكتاب 2)، في حين أن التغيير في العنصر الخارجي (سعر) يؤثر فقط على الكائن الأصلي (كتاب 1).
من ناحية أخرى، يؤدي إنشاء نسخة عميقة إلى إنشاء نسخة مستقلة من الكائن الأصلي، بما في ذلك نسخ جميع الكائنات الموجودة بداخله.
# Create a Book object
book1 = Book("Why MakeUseOf Deep Copy?", \
["Bobby Jack", "Yuvraj Chandra"], 5000)# Make a deep copy
book2 = copy.deepcopy(book1)# Modify the original object
book1.authors.append("Princewill Inyang")
book1.price = 60
# Check the objects
print(book1)
print(book2)
في هذه الحالة، النسخة العميقة (كتاب2) هو كائن مستقل تمامًا، ويتم تعديل الكائن الأصلي (كتاب 1) لا يؤثر عليه.
يستخدم للنسخة الضحلة والنسخة العميقة
من الضروري أن تفهم النسخة العميقة والضحلة حتى تتمكن من تحديد الطريقة المناسبة لمعالجة البيانات. فيما يلي بعض السيناريوهات التي تنطبق فيها كل طريقة:
- استخدم نسخة سطحية إذا كنت تريد نسخ كائن معقد دون إنشاء مثيلات جديدة لكائناته المتداخلة. يعد هذا الأسلوب أكثر كفاءة في استخدام الذاكرة وأسرع من النسخ العميق لأنه لا يكرر الكائنات المتداخلة.
- استخدم نسخة سطحية لإنشاء لقطة لحالة الكائن مع الاستمرار في مشاركة بعض البيانات الأساسية بين الكائنات الأصلية والكائنات المنسوخة.
- استخدم نسخة عميقة إذا كنت تريد تعديل نسخة متماثلة من كائن دون التأثير على الأصل. يؤدي هذا إلى إنشاء نسخ مستقلة من الكائنات المتداخلة، مما يضمن عدم تطبيق أي تغييرات على النسخة على النسخة الأصلية.
- تعد النسخة العميقة أمرًا بالغ الأهمية عندما تحتاج إلى نسخ مستقلة من هياكل البيانات المتداخلة، خاصة عند التعامل مع التسلسلات الهرمية للكائنات المتكررة أو المعقدة.
الأداء والاعتبارات
نظرًا لأن النسخة السطحية لا تنشئ مثيلات جديدة للكائنات المتداخلة، فإنها عادةً ما تعمل بشكل أسرع وتستخدم ذاكرة أقل من النسخة العميقة. ومع ذلك، قد يكون للنسخة الأصلية والسطحية آثار جانبية غير مرغوب فيها نتيجة لتغيير العناصر الداخلية المشتركة.
خاصة بالنسبة لهياكل البيانات الكبيرة والمتداخلة بعمق، والنسخ العميق، إجراء العودي، يمكن أن يكون أبطأ ويستخدم المزيد من الذاكرة. ومع ذلك، فهو يضمن الاستقلال التام بين النسخة الأصلية والنسخة العميقة، مما يجعل معالجة البيانات المعقدة أكثر أمانًا.
أفضل خيار نسخ لبياناتك
تستخدم العديد من لغات البرمجة مفهوم النسخة السطحية والعميقة. يتيح لك فهمها معالجة البيانات دون عواقب غير متوقعة.
باستخدام تقنيات النسخ السطحية والعميقة، يمكنك تحديد أفضل طريقة لتكرار هياكل البيانات الخاصة بك بأمان. من خلال فهم التأثيرات على بياناتك، ستحصل على نتائج أكثر موثوقية ويمكن التنبؤ بها من التعليمات البرمجية الخاصة بك.