سؤال

أنا أقوم بتشغيل Python 2.5 ، لذلك قد لا ينطبق هذا السؤال على Python 3. عندما تقوم بتسلسل هرمي من فئة الماس باستخدام ميراث متعدد وإنشاء كائن من فئة أكثر مشتقة ، يقوم Python بالشيء الصحيح (TM). وتدعو المُنشئ للفئة المشتقة ، ثم فئات الوالدين كما هي مدرجة من اليسار إلى اليمين ، ثم الجد. أنا على دراية ببيثون MRO; ؛ هذا ليس سؤالي. أشعر بالفضول لكيفية إرجاع الكائن من Super في الواقع للتواصل مع مكالمات Super في الفصول الأصل بالترتيب الصحيح. ضع في اعتبارك رمز المثال:

#!/usr/bin/python

class A(object):
    def __init__(self): print "A init"

class B(A):
    def __init__(self):
        print "B init"
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        super(D, self).__init__()

x = D()

الكود يفعل الشيء البديهي ، يطبع:

D init
B init
C init
A init

ومع ذلك ، إذا قمت بتعليق الدعوة إلى Super in BING BISTER ، فلن يتم استدعاء وظيفة init أو C. هذا يعني أن دعوة B إلى Super تدرك بطريقة ما وجود C في التسلسل الهرمي للطبقة الشاملة. أعلم أن Super يعيد كائنًا وكيلًا بمشغل Get Overloaded ، ولكن كيف يتم إرجاع الكائن الذي تم إرجاعه بواسطة Super in D's Intern بوجود C إلى الكائن الذي تم إرجاعه بواسطة Super في تعريف BIP الخاص بـ B؟ هل المعلومات التي يتم تخزين المكالمات اللاحقة للاستخدام الفائق على الكائن نفسه؟ إذا كان الأمر كذلك ، فلماذا لا يكون Super Self.super؟

تحرير: أشار Jekke بحق إلى أنه ليس ذاتيًا. من الناحية المفاهيمية ، هذا أمر منطقي ، ولكن في الممارسة العملية ، ليس سمة الفئة أيضًا! يمكنك اختبار هذا في المترجم المترجم عن طريق إنشاء فئتين A و B ، حيث يرث B من A ، والاتصال dir(B). انه ليس لديها super أو __super__ صفات.

هل كانت مفيدة؟

المحلول

لقد قدمت مجموعة من الروابط أدناه ، والتي تجيب على سؤالك بمزيد من التفصيل وأكثر دقة مما كنت آمل في ذلك. ومع ذلك ، سأقدم إجابة على سؤالك بكلماتي الخاصة أيضًا ، لتوفير بعض الوقت. سأضعه في نقاط -

  1. Super هي وظيفة مبنية ، وليس سمة.
  2. كل يكتب (الفصل) في بيثون لديه __mro__ السمة ، التي تخزن ترتيب دقة الأسلوب لتلك الحالة بالذات.
  3. كل مكالمة إلى Super هي من النموذج Super (اكتب [، كائن أو نوع]). دعنا نفترض أن السمة الثانية هي كائن في الوقت الحالي.
  4. عند نقطة انطلاق المكالمات الفائقة ، يكون الكائن من نوع الفئة المشتقة (قل العاصمة).
  5. يبحث سوبر عن الأساليب التي تتطابق (في حالتك __init__) في الفئات في MRO ، بعد الفئة المحددة كوسيطة أول (في هذه الحالة فئات بعد العاصمة).
  6. عندما يتم العثور على طريقة المطابقة (قل في الفصل BC1)، يدعي.
    ) BC1.
  7. شطف غسل كرر حتى يتم العثور على جميع الطرق ودعاها.

شرح مثالك

 MRO: D,B,C,A,object  
  1. super(D, self).__init__() يسمى. Isinstance (الذات ، د) => صحيح
  2. البحث عن الطريقة التالية في MRO في الفصول إلى يمين D.

    B.__init__ وجدت ودعا


  1. B.__init__ المكالمات super(B, self).__init__().

    Isinstance (الذات ، ب) => خطأ
    Isinstance (الذات ، د) => صحيح

  2. وبالتالي ، فإن MRO هو نفسه ، لكن البحث يستمر إلى يمين B IE C ، A ، يتم البحث في كائن واحدًا تلو الآخر. التالي __init__ تم العثور عليه يسمى.

  3. وهلم جرا وهكذا دواليك.

شرح سوبر
http://www.python.org/download/release/2.2.3/descrintro/#cooperation
أشياء يجب مراقبتها عند استخدام Super
http://fuhm.net/super-harmful/
Pythons MRO خوارزمية:
http://www.python.org/download/release/2.3/mro/
مستندات سوبر:
http://docs.python.org/library/functions.html
يحتوي الجزء السفلي من هذه الصفحة على قسم لطيف على Super:
http://docstore.mik.ua/orelly/other/python/0596001886_pythonian-chp-5-sect-2.html

آمل أن يساعد هذا في توضيح ذلك.

نصائح أخرى

قم بتغيير الكود الخاص بك إلى هذا وأعتقد أنه سيشرح الأشياء (من المفترض super ينظر إلى حيث ، على سبيل المثال ، B في ال __mro__?):

class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()

إذا قمت بتشغيله ، فسترى:

D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)

كما أنه يستحق التحقق Python's Super هو أنيق ، لكن لا يمكنك استخدامه.

just guessing:

self in all the four methods refer to the same object, that is, of class D. so, in B.__init__(), the call to to super(B,self) knows the whole diamond ancestry of self and it has to fetch the method from 'after' B. in this case, it's the C class.

super() knows the full class hierarchy. This is what happens inside B's init:

>>> super(B, self)
<super: <class 'B'>, <D object>>

This resolves the central question,

how does the object returned by super in D's init definition communicate the existence of C to the object returned by super in B's init definition?

Namely, in B's init definition, self is an instance of D, and thus communicates the existence of C. For example C can be found in type(self).__mro__.

Jacob's answer shows how to understand the problem, while batbrat's shows the details and hrr's goes straight to the point.

One thing they do not cover (at least not explicity) from your question is this point:

However, if you comment out the call to super in B's init function, neither A nor C's init function is called.

To understand that, change Jacob's code to to print the stack on A's init, as below:

import traceback

class A(object):
    def __init__(self):
        print "A init"
        print self.__class__.__mro__
        traceback.print_stack()

class B(A):
    def __init__(self):
        print "B init"
        print self.__class__.__mro__
        super(B, self).__init__()

class C(A):
    def __init__(self):
        print "C init"
        print self.__class__.__mro__
        super(C, self).__init__()

class D(B, C):
    def __init__(self):
        print "D init"
        print self.__class__.__mro__
        super(D, self).__init__()

x = D()

It is a bit surprising to see that B's line super(B, self).__init__() is actually calling C.__init__(), as C is not a baseclass of B.

D init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
B init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
C init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
A init
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <type 'object'>)
  File "/tmp/jacobs.py", line 31, in <module>
    x = D()
  File "/tmp/jacobs.py", line 29, in __init__
    super(D, self).__init__()
  File "/tmp/jacobs.py", line 17, in __init__
    super(B, self).__init__()
  File "/tmp/jacobs.py", line 23, in __init__
    super(C, self).__init__()
  File "/tmp/jacobs.py", line 11, in __init__
    traceback.print_stack()

This happens because super (B, self) is not 'calling the B's baseclass version of __init__'. Instead, it is 'calling __init__ on the first class to the right of B that is present on self's __mro__ and that has such an attribute.

So, if you comment out the call to super in B's init function, the method stack will stop on B.__init__, and will never reach C or A.

To summarize:

  • Regardless of which class is referring to it, self is always a reference to the instance, and its __mro__ and __class__ remain constant
  • super() finds the method looking to the classes that are to the right of the current one on the __mro__. As the __mro__ remains constant, what happens is that it is searched as a list, not as a tree or a graph.

On that last point, note that the full name of the MRO's algorithm is C3 superclass linearization. That is, it flattens that structure into a list. When the different super() calls happen, they are effectivelly iterating that list.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top