سؤال

class A(object):
    def __init__(self, a, b, c):
        #super(A, self).__init__()
        super(self.__class__, self).__init__()


class B(A):
    def __init__(self, b, c):
        print super(B, self)
        print super(self.__class__, self)
        #super(B, self).__init__(1, b, c)
        super(self.__class__, self).__init__(1, b, c)

class C(B):
    def __init__(self, c):
        #super(C, self).__init__(2, c)
        super(self.__class__, self).__init__(2, c)
C(3)

في الكود أعلاه ، علق __init__ تظهر المكالمات إلى أن تكون الطريقة "الذكية" المقبولة بشكل شائع للقيام بالهيئة الفائقة. ومع ذلك ، في حالة من المحتمل أن يتغير التسلسل الهرمي للصف ، كنت أستخدم النموذج غير المعقول ، حتى وقت قريب.

يبدو أنه في المكالمة إلى مُنشئ Super for B في التسلسل الهرمي أعلاه ، ذلك B.__init__ يسمى مرة أخرى ، self.__class__ هو في الواقع C, ، ليس B كما افترضت دائما.

هل هناك بطريقة ما في Python-2.x يمكنني الحفاظ على MRO المناسبة (فيما يتعلق بتهيئة جميع فئات الأصل بالترتيب الصحيح) عند استدعاء المنشآت الفائقة مع عدم تسمية الفئة الحالية ( B في super(B, self).__init__(1, b, c))?

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

المحلول

إجابة قصيرة: لا ، لا توجد طريقة لاستدعاء الحق ضمنيًا __init__ مع الحجج الصحيحة لفئة الوالدين الصحيحة في بيثون 2.x.

بالمناسبة ، فإن الرمز كما هو موضح هنا غير صحيح: إذا كنت تستخدم Super ().__init__, ، ثم يجب أن يكون لجميع الفصول في التسلسل الهرمي نفس التوقيع في __init__ أساليب. وإلا يمكن أن يتوقف التعليمات البرمجية الخاصة بك عن العمل إذا قمت بتقديم فئة فرعية جديدة تستخدم الميراث المتعدد.

يرى http://fuhm.net/super-harmful/ للحصول على وصف أطول للقضية (مع الصور).

نصائح أخرى

الرمز الخاص بك لا علاقة له بترتيب قرار الطريقة. يأتي حل الطريقة في حالة الميراث المتعدد الذي ليس هو الحال في مثالك. الكود الخاص بك خطأ ببساطة لأنك تفترض ذلك self.__class__ هو في الواقع نفس الفئة من الفئة التي يتم فيها تحديد الطريقة وهذا خطأ:

>>> class A(object):
...     def __init__(self):
...         print self.__class__
... 
>>> 
>>> class B(A):
...     def __init__(self):
...         A.__init__(self)
... 
>>> B()
<class '__main__.B'>
<__main__.B object at 0x1bcfed0>
>>> A()
<class '__main__.A'>
<__main__.A object at 0x1bcff90>
>>> 

لذلك عندما يجب عليك الاتصال:

super(B, self).__init__(1, b, c)

أنت تتصل بالفعل:

# super(self.__class__, self).__init__(1, b, c)
super(C, self).__init__(1, b, c)

تعديل: تحاول الإجابة على السؤال بشكل أفضل.

class A(object):
    def __init__(self, a):
        for cls in self.__class__.mro():
            if cls is not object:
                cls._init(self, a)
    def _init(self, a):
        print 'A._init'
        self.a = a

class B(A):
    def _init(self, a):
        print 'B._init'

class C(A):
    def _init(self, a):
        print 'C._init'

class D(B, C):
    def _init(self, a):
        print 'D._init'


d = D(3)
print d.a

مطبوعات:

D._init
B._init
C._init
A._init
3

(نسخة معدلة من نمط القالب).

الآن تسمى أساليب الوالدين ضمنيًا ، لكن يجب أن أتفق مع Python Zen حيث يكون الصريح أفضل من الضمني لأن الكود أقل قابلية للقراءة والمكسب ضعيف. لكن احذروا كل شيء _init الأساليب لها نفس المعلمات ، لا يمكنك نسيان الآباء تمامًا ولا أقترح القيام بذلك.

للميراث الفردي ، فإن النهج الأفضل هو استدعاء طريقة الوالد بشكل صريح ، دون التذرع super. القيام بذلك ليس عليك ذلك قم بتسمية الفصل الحالي, ، ولكن لا يزال يجب أن تهتم بمن هو فئة الوالدين.

القراءات الجيدة هي: كيف-بيثونز-سوبر-دو-اليمين والروابط المقترحة في هذا السؤال وخاصة Python's Super هو أنيق ، لكن لا يمكنك استخدامه

إذا كان من المحتمل أن يتغير التسلسل الهرمي أعراض التصميم السيئ وله عواقب في جميع الأجزاء التي تستخدم هذا الرمز ولا ينبغي تشجيعها.

تحرير 2

مثال آخر يأتيني في الاعتبار ، ولكنه يستخدم metaclasses. مكتبة أورويد يستخدم metaclass لتخزين سمة ، __super, ، في الفصل حتى تحتاج فقط للوصول إلى تلك السمة.

السابق:

>>> class MetaSuper(type):
...     """adding .__super"""
...     def __init__(cls, name, bases, d):
...         super(MetaSuper, cls).__init__(name, bases, d)
...         if hasattr(cls, "_%s__super" % name):
...             raise AttributeError, "Class has same name as one of its super classes"
...         setattr(cls, "_%s__super" % name, super(cls))
... 
>>> class A:
...  __metaclass__ = MetaSuper
...  def __init__(self, a):
...   self.a = a
...   print 'A.__init__'
... 
>>> class B(A):
...  def __init__(self, a):
...   print 'B.__init__'
...   self.__super.__init__(a)
... 
>>> b = B(42)
B.__init__
A.__init__
>>> b.a
42
>>> 

ربما ما تبحث عنه هو metaclasses؟

class metawrap(type):
    def __new__(mcs,name, bases, dict):
        dict['bases'] = bases
        return type.__new__(mcs,name,bases,dict)

class A(object):
    def __init__(self):
        pass
    def test(self):
        print "I am class A"

class B(A):
    __metaclass__ = metawrap
    def __init__(self):
        pass
    def test(self):
        par = super(self.bases[0],self)
        par.__thisclass__.test(self)
foo = B()
foo.test()

المطبوعات "أنا الفئة أ"

ما يفعله metaclass هو تجاوز الإنشاء الأولي للفئة B (وليس الكائن) ويتأكد من أن القاموس المبني لكل كائن B يحتوي الآن على صفيف قواعد حيث يمكنك العثور

على حد علمي ، ما يلي لم يتم ذلك بشكل شائع. لكن يبدو أنه يعمل.

الأساليب في تعريف فئة معين دائمًا ، قم بتشغيل سمات inderscore المزدوجة لتشمل اسم الفصل الذي تم تعريفه فيه. لذا ، إذا قمت بتبشير مرجع إلى الفئة في النموذج الذي تتبادل فيه الأسم ذلك في الدعوة إلى super.

مثال على خداع المراجع على الكائن نفسه ، من خلال التنفيذ __new__ على baseclass:

def mangle(cls, name):
    if not name.startswith('__'):
        raise ValueError('name must start with double underscore')
    return '_%s%s' % (cls.__name__, name)

class ClassStasher(object):
    def __new__(cls, *args, **kwargs):
        obj = object.__new__(cls)
        for c in cls.mro():
            setattr(obj, mangle(c, '__class'), c)
        return obj

class A(ClassStasher):
    def __init__(self):
        print 'init in A', self.__class
        super(self.__class, self).__init__()

class B(A):
    def __init__(self):
        print 'init in B', self.__class
        super(self.__class, self).__init__()

class C(A):
    def __init__(self):
        print 'init in C', self.__class
        super(self.__class, self).__init__()

class D(B, C):
    def __init__(self):
        print 'init in D', self.__class
        super(self.__class, self).__init__()


d = D()    
print d

والقيام بشيء مشابه ، ولكن باستخدام فئة تلوي وخبز __class المراجع على كائنات الفصل نفسها:

class ClassStasherType(type):
    def __init__(cls, name, bases, attributes):
        setattr(cls, mangle(cls, '__class'), cls)

class ClassStasher(object):
    __metaclass__ = ClassStasherType

class A_meta(ClassStasher):
    def __init__(self):
        print 'init in A_meta', self.__class
        super(self.__class, self).__init__()

class B_meta(A_meta):
    def __init__(self):
        print 'init in B_meta', self.__class
        super(self.__class, self).__init__()

class C_meta(A_meta):
    def __init__(self):
        print 'init in C_meta', self.__class
        super(self.__class, self).__init__()

class D_meta(B_meta, C_meta):
    def __init__(self):
        print 'init in D_meta', self.__class
        super(self.__class, self).__init__()


d = D_meta()    
print d

تشغيل كل هذا معًا ، كملف مصدر واحد:

% python /tmp/junk.py
init in D <class '__main__.D'>
init in B <class '__main__.B'>
init in C <class '__main__.C'>
init in A <class '__main__.A'>
<__main__.D object at 0x1004a4a50>
init in D_meta <class '__main__.D_meta'>
init in B_meta <class '__main__.B_meta'>
init in C_meta <class '__main__.C_meta'>
init in A_meta <class '__main__.A_meta'>
<__main__.D_meta object at 0x1004a4bd0>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top