اذهب إلى المحتوى

السؤال

نشر

أحاول التطبيق على OOP ولكن عند إستخدام المعامل += يحدث أمر غير متوقع، في الكود التالي يوجد صنفين foo و foo2 وعند عمل كائن من كل صنف وإضافة قيمة إلى القائمة bar يتم إضافتها أيضًا إلى الكائن الآخر!!

>>> class foo:
...     bar = []
...     def __init__(self,x):
...             self.bar += [x]
...
>>> class foo2:
...     bar = []
...     def __init__(self,x):
...             self.bar = self.bar + [x]
...
>>> f = foo(1)
>>> g = foo(2)
>>> f.bar
[1, 2]
>>> g.bar
[1, 2]
>>> f.bar += [3]
>>> f.bar
[1, 2, 3]
>>> g.bar
[1, 2, 3]

لماذا أصبح الكائن g يحتوي على نفس القائمة أيضًا؟

هذه المشكلة لا تحدث إن قمت بإستخدام الطريقة العادية كالتالي:

>>> f.bar = f.bar + [4]
>>> f.bar
[1, 2, 3, 4]
>>> g.bar
[1, 2, 3]
>>>

ما الفرق بين الطريقة الأولى والثانية؟

Recommended Posts

  • 1
نشر

السبب في اختلاف الدالة التي يتم استدعائها، حيث أن =+ في مثالك:

>>> f.bar += [3]

تقوم باستدعاء __iadd__ التي تعدل f.bar الأصلية.

أما:

>>> f.bar = f.bar + [4]

تقوم بإنشاء قائمة جديدة ثم إسنادها للمتغير حيث أنها تستدعي الدالة __add__

  • 0
نشر

هناك نوعين من الخواص في بايثون هما : class attributes and instance attributes, حيث class attributes تتشارك نفس القيمة مع كل ال object يتم انشاءه من الصنف, أما ال instance attributes فهي فقط تنتمي للobject , انت هناك قمت بتعريف المتغير bar على أنه class attributes ولذلك سوف تتشارك جميع الكائنات قيمته, الحل أن نقوم بتعريف المتغير بداخل الكونستركتور كالتالي

class foo:
     def __init__(self,x):
        self.bar = []
        self.bar += [x]

الآن لو حاولت انشاء أكثر من كائن من نفس الصنف لن تتشارك الكائنات نفس القيمة

  • 0
نشر

كما أشار @محمد أبو عواد و @Wael Aljamal.
أولاً أنت تعرف القائمة bar على أنها  class attributes أي وكأنها static أي تتشارك القيمة مع كل الكائنات المأخوذة من الصف.
ثانياً في حالة استخادم += فهذا يقابل استدعاء الدالة  __iadd__ التي تقوم بتعديل القيمة الأصلية لل bar. أي أنها تغير قيمة الكائن الذي تعمل عليه.
أما  + فهذا يقابل استدعاء الدالة __add__ التي تقوم بإنشاء كائن جديد، أي في حالتنا سوف تنشئ نسخة من bar أي أن التعديل لن يتأثر به المتغير bar الأصلي.
ويمكنك التأكد بذلك عن طريق تجربة التالي:

>>> f.bar = f.bar + [4]
>>> print(f.bar) # [1, 2, 3, 4]
>>> print(g.bar)  # [1, 2, 3]
>>> foo.bar # [1, 2, 3]

مع ملاحظة أن التابع الخاص __iadd__ يحاول في البداية أن يعدل القيمة الأصلية للمتغير، وإذا لم ينجح (مثلاً قد يكون متحولاً ثابتاً أي لايمكن تعديل قيمته) فإنه يقوم بإنشاء نسخة جديدة منه، أي يسلك سلوك __add__.

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...