2016-01-14 8 views
6

में __init__ को ओवरराइड करने के लिए __new__ का उपयोग करना, मुझे __new__ कार्यक्षमता का उपयोग करने में दिलचस्पी है __init__ सबक्लास के कार्य में कोड इंजेक्ट करने के लिए। प्रलेखन से मेरी समझ यह है कि __new__ द्वारा लौटाए गए उदाहरण पर पायथन __init__ पर कॉल करेगा। हालांकि, __new__ से इसे वापस करने से पहले उदाहरण में __init__ के मान को बदलने के मेरे प्रयासों को काम नहीं लगता है।सबक्लास

class Parent(object): 

    def __new__(cls, *args, **kwargs): 
     new_object = super(Parent, cls).__new__(cls) 
     user_init = new_object.__init__ 
     def __init__(self, *args, **kwargs): 
      print("New __init__ called") 
      user_init(self, *args, **kwargs) 
      self.extra() 
     print("Replacing __init__") 
     setattr(new_object, '__init__', __init__) 
     return new_object 

    def extra(self): 
     print("Extra called") 

class Child(Parent): 

    def __init__(self): 
     print("Original __init__ called") 
     super(Child, self).__init__() 

c = Child() 

ऊपर कोड प्रिंट:

Replacing __init__ 
Original __init__ called 

लेकिन मैं इसे

Replacing __init__ 
New __init__ called 
Original __init__ called 
Extra called 

क्यों नहीं मुद्रित करने के लिए उम्मीद करेंगे?

मुझे लगता है कि पाइथन __init__ के मूल मूल्य को कॉल कर रहा है, भले ही मैंने इसे __new__ में सेट किया हो। c.__init__ पर आत्मनिरीक्षण चलाने से पता चलता है कि नया संस्करण जगह पर है, लेकिन इसे ऑब्जेक्ट सृजन का हिस्सा नहीं कहा गया है।

+0

आपका प्रश्न क्या है? – cat

उत्तर

0

मुझे संदेह है कि __init__ एक विशेष कार्य है, आंतरिक रूप से इसे क्लास विधि के रूप में परिभाषित किया जाता है, और नतीजतन इसे ऑब्जेक्ट के उदाहरण में पुन: असाइन करके प्रतिस्थापित नहीं किया जा सकता है।

पायथन में, सभी ऑब्जेक्ट्स को PyObject सी में दर्शाया गया है, जिसमें PyTypeObject पर पॉइंटर है। इसमें tp_init नामक एक सदस्य शामिल है जो मेरा मानना ​​है कि __init__ फ़ंक्शन में पॉइंटर शामिल है।

अन्य समाधान काम करता है, क्योंकि हम कक्षा को संशोधित कर रहे हैं, ऑब्जेक्ट का उदाहरण नहीं।

+0

'PyTypeObject'' टाइप' ऑब्जेक्ट का प्रतिनिधित्व करता है, सभी ऑब्जेक्ट्स नहीं, नहीं? –

+0

अरग। जीथ्यूब के माध्यम से स्रोत कोड में 'PyObject'' की तलाश में था और इसके बजाय' PyTypeObject'' मिला। एक 'PyObject'' में '' ob_type'' विधि है जो 'PyTypeObject'' को इंगित करती है, इसलिए मुझे लगता है कि मेरा तर्क अभी भी खड़ा हो सकता है। यह वास्तव में इस बात पर निर्भर करता है कि '' __init__'' विधि को कैसे कहा जाता है। –

+0

कोशिश करने और सरल बनाने के लिए मेरे उत्तर को दोबारा दोहराएं। –

1

ठीक है, नई वस्तु __init__ कहने से पहले खाली होने की उम्मीद है। तो संभवतः पाइथन, अनुकूलन के रूप में, ऑब्जेक्ट से पूछताछ करने के लिए परेशान नहीं है और सीधे कक्षा से __init__ लाने के लिए जाता है।

इसलिए आपको उप-वर्गों के __init__ को संशोधित करना होगा। सौभाग्य से पायथन के लिए उस उपकरण के लिए एक उपकरण है, मेटाक्लास।

अजगर 2 में, आप विशेष सदस्य की स्थापना करके metaclass सेट:

class Parent(object): 
    __metaclass__ = Meta 
    ... 

देखें Python2 documentation

अजगर 3 में, आप metaclass माता पिता की सूची में कीवर्ड विशेषता के माध्यम से निर्धारित करते हैं, तो

class Parent(metaclass=Meta): 
    ... 

देखें Python3 documentation

मुझे क्लास उदाहरण के लिए टैक्लास एक बेस क्लास है। इसे type से लिया जाना चाहिए और इसमें __new__ में यह वर्ग बनाया जा सकता है (मुझे विश्वास है कि __init__ भी कहा जाना चाहिए, लेकिन उदाहरण __new__ ओवरराइड करते हैं, इसलिए मैं इसके साथ जाऊंगा)।तुम्हारे पास क्या है __new__ के समान होगा:

class Meta(type): 
    def __new__(mcs, name, bases, namespace, **kwargs): 
     new_cls = super(Meta, mcs).__new__(mcs, name, bases, namespace, **kwargs) 
     user_init = new_cls.__init__ 
     def __init__(self, *args, **kwargs): 
      print("New __init__ called") 
      user_init(self, *args, **kwargs) 
      self.extra() 
     print("Replacing __init__") 
     setattr(new_cls, '__init__', __init__) 
     return new_cls 

(अजगर 3 उदाहरण का उपयोग, लेकिन अजगर 2 में हस्ताक्षर को छोड़कर कोई **kwargs हैं एक ही प्रतीत हो रहा है, लेकिन उन्हें जोड़ने से चोट नहीं चाहिए, मैं इसका परीक्षण नहीं किया)।