__Init __ () को हमेशा __new __ () के बाद क्यों कहा जाता है?


568

मैं बस अपनी एक कक्षा को सुव्यवस्थित करने की कोशिश कर रहा हूं और उसी तरह की शैली में कुछ कार्यक्षमता पेश की है जैसे कि फ्लाईवेट डिजाइन पैटर्न

हालांकि, मैं थोड़ा उलझन में हूं कि __init__हमेशा बाद में क्यों बुलाया जाता है __new__। मुझे इसकी उम्मीद नहीं थी। क्या कोई मुझे बता सकता है कि ऐसा क्यों हो रहा है और मैं इस कार्यक्षमता को अन्यथा कैसे लागू कर सकता हूं? (कार्यान्वयन को लागू करने के अलावा, __new__जो काफी हैक करने योग्य लगता है।)

यहाँ एक उदाहरण है:

class A(object):
    _dict = dict()

    def __new__(cls):
        if 'key' in A._dict:
            print "EXISTS"
            return A._dict['key']
        else:
            print "NEW"
            return super(A, cls).__new__(cls)

    def __init__(self):
        print "INIT"
        A._dict['key'] = self
        print ""

a1 = A()
a2 = A()
a3 = A()

आउटपुट:

NEW
INIT

EXISTS
INIT

EXISTS
INIT

क्यों?


के रूप में अच्छी तरह से डिजाइन पैटर्न को समझने की कोशिश कर रहा था, और पहली बार के बारे में सुना: फ्लाईवेट डिजाइन पैटर्न .. और बहुत अच्छा लिंक लगभग सभी लोकप्रिय भाषा में उदाहरण है।
एम्मायांग

जवाबों:


598

उपयोग करें __new__जब आपको एक नई आवृत्ति के निर्माण को नियंत्रित करने की आवश्यकता होती है।

__init__जब आपको किसी नए उदाहरण के प्रारंभ को नियंत्रित करने की आवश्यकता हो तब उपयोग करें ।

__new__उदाहरण निर्माण का पहला चरण है। इसे पहले कहा जाता है, और अपनी कक्षा के एक नए उदाहरण को वापस करने के लिए जिम्मेदार है।

इसके विपरीत, __init__कुछ भी वापस नहीं करता है; इसके बनने के बाद केवल इंस्टेंस को इनिशियलाइज़ करना ही ज़िम्मेदार है।

सामान्य तौर पर, आपको __new__तब तक ओवरराइड करने की आवश्यकता नहीं होनी चाहिए जब तक कि आप एक अपरिवर्तनीय प्रकार जैसे str, int, unicode या tuple को उपवर्गित नहीं कर रहे हैं।

अप्रैल 2008 पोस्ट से: बनाम कब उपयोग करें ? __new____init__mail.python.org पर।

आपको यह विचार करना चाहिए कि आप जो करने की कोशिश कर रहे हैं वह आमतौर पर एक कारखाने के साथ किया जाता है और इसे करने का सबसे अच्छा तरीका है। उपयोग करना __new__एक अच्छा साफ समाधान नहीं है, इसलिए कृपया किसी कारखाने के उपयोग पर विचार करें। यहां आपके पास एक अच्छा कारखाना उदाहरण है


1
__new__एक फ़ैक्टरी क्लास के अंदर का उपयोग करके समाप्त हुआ , जो काफी साफ हो गया है, इसलिए आपके इनपुट के लिए धन्यवाद।
डैन

11
क्षमा करें, मैं असहमत हूं कि उपयोग किए __new__गए मामलों को कड़ाई से सीमित किया जाना चाहिए। मैंने एक्स्टेंसिबल जेनेरिक क्लास फैक्ट्रियों को लागू करने के लिए इसे बहुत उपयोगी पाया है - मेरे सवाल का जवाब देखें पायथन में कक्षाएं उत्पन्न करने के __new__लिए अनुचित उपयोग ? ऐसा करने के एक उदाहरण के लिए।
मार्टीन्यू

170

__new__स्थिर वर्ग विधि है, जबकि __init__उदाहरण विधि है। __new__पहले उदाहरण बनाना है, इसलिए __init__इसे इनिशियलाइज़ कर सकते हैं। ध्यान दें कि पैरामीटर के रूप में __init__लेता है self। जब तक आप उदाहरण नहीं बनाते तब तक कोई नहीं है self

अब, मैं इकट्ठा करता हूं, कि आप पायथन में सिंगलटन पैटर्न को लागू करने की कोशिश कर रहे हैं । ऐसा करने के कुछ तरीके हैं।

इसके अलावा, पायथन 2.6 के रूप में, आप वर्ग सज्जाकारों का उपयोग कर सकते हैं ।

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
  ...

8
@ टायलर लोंग, मुझे यह समझ में नहीं आया कि "@ सिंगिंगटन" कैसे काम करता है? क्योंकि डेकोरेटर एक फंक्शन देता है लेकिन MyClass एक क्लास है।
Alcott

11
शब्दकोश क्यों? चूंकि cls हमेशा समान रहेंगे और आपको प्रत्येक सिंगलटन के लिए उदाहरण के लिए एक नया शब्दकोश मिलेगा, जिसमें आप केवल एक आइटम बना सकते हैं।
विंस्टन ईवर्ट

7
@ ऑलकोट: कोई राय की जरूरत नहीं है - डॉक्स आपसे सहमत हैं।
एथन फुरमैन

2
@Alcott। हाँ डेकोरेटर एक फ़ंक्शन देता है। लेकिन दोनों वर्ग और फ़ंक्शन एक कॉल करने योग्य हैं। मुझे लगता है कि उदाहरण = {} एक वैश्विक चर होना चाहिए।
टायलर लॉन्ग

4
@ टायलरलॉन्ग अल्कोट का एक अच्छा बिंदु है। इसका परिणाम MyClassउस फ़ंक्शन से जुड़ा होता है जो मूल वर्ग के उदाहरणों को लौटाता है। लेकिन अब मूल कक्षा को संदर्भित करने का कोई तरीका नहीं है, और MyClassफ़ंक्शन का टूटना isinstance/ issubclassचेक होना, क्लास की विशेषताओं / विधियों तक सीधे पहुंच MyClass.something, superकॉल, आदि में क्लास का नामकरण , आदि। यह समस्या है या नहीं, इस पर निर्भर करता है वर्ग आप इसे (और कार्यक्रम के बाकी) के लिए आवेदन कर रहे हैं।
बेन

148

अधिकांश प्रसिद्ध ओओ भाषाओं में, एक अभिव्यक्ति की तरह SomeClass(arg1, arg2)एक नया उदाहरण आवंटित करेगा, उदाहरण की विशेषताओं को इनिशियलाइज़ करेगा, और फिर इसे वापस करेगा।

अधिकांश प्रसिद्ध OO भाषाओं में, "उदाहरण की विशेषताओं को इनिशियलाइज़ करें" भाग को एक कंस्ट्रक्टर को परिभाषित करके प्रत्येक वर्ग के लिए अनुकूलित किया जा सकता है , जो मूल रूप से कोड का एक ब्लॉक है जो नए इंस्टेंस पर काम करता है (कंस्ट्रक्टर एक्सप्रेशन के लिए दिए गए तर्कों का उपयोग करके) ) जो भी प्रारंभिक शर्तें वांछित हैं, उन्हें स्थापित करने के लिए। पायथन में, यह कक्षा की __init__विधि से मेल खाती है ।

पायथन का __new__"एक नया उदाहरण आवंटित करें" भाग के समान प्रति-वर्ग अनुकूलन से कम और कुछ भी नहीं है। यह निश्चित रूप से आपको असामान्य चीजें करने की अनुमति देता है जैसे कि किसी नए को आवंटित करने के बजाय मौजूदा उदाहरण को वापस करना। इसलिए पायथन में, हमें वास्तव में इस हिस्से के बारे में नहीं सोचना चाहिए क्योंकि आवश्यक रूप से आवंटन शामिल है; हम सभी की आवश्यकता है कि __new__कहीं से एक उपयुक्त उदाहरण के साथ आता है।

लेकिन यह अभी भी नौकरी का केवल आधा हिस्सा है, और पायथन प्रणाली के लिए यह जानने का कोई तरीका नहीं है कि कभी-कभी आप नौकरी के दूसरे भाग ( __init__) को बाद में चलाना चाहते हैं और कभी-कभी आप नहीं करते हैं। यदि आप उस व्यवहार को चाहते हैं, तो आपको स्पष्ट रूप से कहना होगा।

अक्सर, आप रिफ्लेक्टर कर सकते हैं ताकि आपको केवल ज़रूरत हो __new__, या इसलिए आपको ज़रूरत नहीं है __new__, या इसलिए कि __init__पहले से ही एक प्रारंभिक ऑब्जेक्ट पर अलग तरह से व्यवहार करता है। लेकिन अगर आप वास्तव में चाहते हैं, तो पायथन वास्तव में आपको "नौकरी" को फिर से परिभाषित करने की अनुमति देता है, ताकि SomeClass(arg1, arg2)जरूरी कॉल __new__उसके बाद न हो __init__। ऐसा करने के लिए, आपको मेटाक्लस बनाने की जरूरत है, और इसकी __call__विधि को परिभाषित करें ।

एक मेटाक्लास सिर्फ एक वर्ग का वर्ग है। और एक वर्ग की __call__विधि नियंत्रित करती है कि जब आप कक्षा के उदाहरण कहते हैं तो क्या होता है। तो एक metaclass ' __call__विधि नियंत्रण क्या होता है जब आप एक वर्ग कहते हैं; यानी यह आपको शुरू से अंत तक इंस्टेंस-निर्माण तंत्र को फिर से परिभाषित करने की अनुमति देता है । यह वह स्तर है जिस पर आप एकल स्तर पर गैर-मानक आवृत्ति निर्माण प्रक्रिया जैसे कि सिंगलटन पैटर्न को सर्वाधिक प्रभावी ढंग से लागू कर सकते हैं। वास्तव में, कोड की 10 से कम लाइनों के साथ आप एक Singletonमेटाकाॅल को लागू कर सकते हैं जो तब आपको __new__ सभी के साथ फ्यूज करने की आवश्यकता नहीं होती है , और किसी भी अन्यथा-सामान्य वर्ग को एक एकल में केवल जोड़कर बदल सकते हैं __metaclass__ = Singleton!

class Singleton(type):
    def __init__(self, *args, **kwargs):
        super(Singleton, self).__init__(*args, **kwargs)
        self.__instance = None
    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            self.__instance = super(Singleton, self).__call__(*args, **kwargs)
        return self.__instance

हालांकि यह शायद इस स्थिति के लिए वास्तव में वारंट की तुलना में गहरा जादू है!


25

प्रलेखन उद्धृत करने के लिए :

विशिष्ट कार्यान्वयन "सुपर (करेंटक्लास, cls) .__ नए __ (cls [, ...])" का उपयोग करके सुपरक्लास की __new __ () पद्धति का उपयोग करके वर्ग का एक नया उदाहरण बनाते हैं। उपयुक्त तर्कों के साथ और फिर नव-निर्मित उदाहरण को आवश्यक रूप से संशोधित करते हैं। इसे वापस करने से पहले।

...

यदि __new __ () cls का एक उदाहरण नहीं लौटाता है, तो नए उदाहरण के __init __ () विधि को लागू नहीं किया जाएगा।

__new __ () का उद्देश्य मुख्य रूप से उदाहरण के निर्माण को अनुकूलित करने के लिए अपरिवर्तनीय प्रकारों (जैसे int, str, या tuple) की उपवर्गों की अनुमति देना है।


18
If __new__() does not return an instance of cls, then the new instance's __init__() method will not be invoked.यह एक महत्वपूर्ण बिंदु है। यदि आप एक अलग उदाहरण लौटाते हैं, तो मूल __init__कभी नहीं कहा जाता है।
जेफरी जोस

@ मुझे इस बात का एहसास है कि आपका उत्तर डॉक्स से है, लेकिन मुझे उत्सुकता है अगर आप किसी उदाहरण का उपयोग करने के मामले को जानते हैं। ऐसा लगता है कि जब __new__अपनी कक्षा के लिए लौटी हुई वस्तु की जाँच की जाती है, तो विधि विफल होने के बजाय चुपचाप पास होने की अनुमति देने के बजाय एक त्रुटि फेंक देगी, क्योंकि मुझे समझ में नहीं आता कि आप कभी भी कुछ भी वापस करना चाहते हैं लेकिन कक्षा की एक वस्तु ।
soporific312

@ soporific312 मैंने कोई उपयोग-मामले नहीं देखे हैं। यह उत्तर डिजाइन के लिए कुछ तर्क पर चर्चा करता है, हालांकि उन्होंने उस "सुविधा" का लाभ उठाने वाले किसी भी कोड को नहीं देखा है।
tgray

12

मुझे एहसास है कि यह सवाल काफी पुराना है, लेकिन मेरे पास एक समान मुद्दा था। निम्नलिखित ने वही किया जो मैं चाहता था:

class Agent(object):
    _agents = dict()

    def __new__(cls, *p):
        number = p[0]
        if not number in cls._agents:
            cls._agents[number] = object.__new__(cls)
        return cls._agents[number]

    def __init__(self, number):
        self.number = number

    def __eq__(self, rhs):
        return self.number == rhs.number

Agent("a") is Agent("a") == True

मैंने इस पृष्ठ का उपयोग एक संसाधन के रूप में किया http://infohost.nmt.edu/tcc/help/pubs/python/web/new-new-method.html


7
नोट: __new__ हमेशा एक उपयुक्त वस्तु देता है, इसलिए __init__ को हमेशा कहा जाता है - भले ही उदाहरण पहले से मौजूद हो।
विषम

10

मुझे लगता है कि इस प्रश्न का सरल उत्तर यह है कि, यदि __new__कोई मान उसी प्रकार का होता है जो वर्ग के समान होता है, तो __init__फ़ंक्शन निष्पादित होता है, अन्यथा यह नहीं होगा। इस स्थिति में आपका कोड वापस आ A._dict('key')जाता है जो उसी वर्ग का है cls, इसलिए __init__निष्पादित किया जाएगा।


10

जब __new__एक ही वर्ग का रिटर्न उदाहरण, __init__बाद में लौटे ऑब्जेक्ट पर चलाया जाता है। यानी आप चलाने से __new__रोकने के लिए उपयोग नहीं कर सकते __init__। यहां तक ​​कि अगर आप पहले से बनाई गई वस्तु से लौटते हैं __new__, तो यह __init__फिर से और फिर से शुरुआती (दोहराए जाने वाले ...) से दोगुना होगा ।

यहाँ सिंगलटन पैटर्न के लिए सामान्य दृष्टिकोण है जो ऊपर दिए गए vartec उत्तर को बढ़ाता है और इसे ठीक करता है:

def SingletonClass(cls):
    class Single(cls):
        __doc__ = cls.__doc__
        _initialized = False
        _instance = None

        def __new__(cls, *args, **kwargs):
            if not cls._instance:
                cls._instance = super(Single, cls).__new__(cls, *args, **kwargs)
            return cls._instance

        def __init__(self, *args, **kwargs):
            if self._initialized:
                return
            super(Single, self).__init__(*args, **kwargs)
            self.__class__._initialized = True  # Its crucial to set this variable on the class!
    return Single

पूरी कहानी है यहाँ

एक अन्य दृष्टिकोण, जिसमें वास्तव में __new__क्लासमेथोड का उपयोग करना शामिल है:

class Singleton(object):
    __initialized = False

    def __new__(cls, *args, **kwargs):
        if not cls.__initialized:
            cls.__init__(*args, **kwargs)
            cls.__initialized = True
        return cls


class MyClass(Singleton):
    @classmethod
    def __init__(cls, x, y):
        print "init is here"

    @classmethod
    def do(cls):
        print "doing stuff"

कृपया ध्यान दें, इस दृष्टिकोण के साथ आपको अपने सभी तरीकों को सजाने की आवश्यकता है @classmethod, क्योंकि आप कभी भी किसी भी वास्तविक उदाहरण का उपयोग नहीं करेंगे MyClass


6

इस डॉक्टर का जिक्र :

जब संख्याओं और तारों जैसे अपरिवर्तनीय अंतर्निहित प्रकारों को उप-वर्गित किया जाता है, और कभी-कभी अन्य स्थितियों में, स्थिर विधि नया काम में आता है। नई उदाहरण निर्माण में पहला कदम है, इससे पहले लागू किया है init

नई विधि अपना पहला तर्क के रूप में वर्ग के साथ कहा जाता है; इसकी जिम्मेदारी उस वर्ग का एक नया उदाहरण लौटाना है।

इस init की तुलना करें : init को इसके पहले तर्क के रूप में एक उदाहरण के साथ कहा जाता है, और यह कुछ भी वापस नहीं करता है; इसकी जिम्मेदारी उदाहरण को शुरू करना है।

स्थितियों में, जहां एक नया उदाहरण बुला बिना बनाई गई हो रहे हैं init (उदाहरण के लिए जब उदाहरण के एक अचार से भरी हुई है)। बुला के बिना एक नया उदाहरण बनाने के लिए कोई तरीका नहीं है नया (हालांकि कुछ मामलों में आपको बुला रहा एक आधार वर्ग के साथ प्राप्त कर सकते हैं नया )।

आप जो हासिल करना चाहते हैं, उसके बारे में भी सिंगलटन पैटर्न के बारे में उसी डॉक्टर की जानकारी है

class Singleton(object):
        def __new__(cls, *args, **kwds):
            it = cls.__dict__.get("__it__")
            if it is not None:
                return it
            cls.__it__ = it = object.__new__(cls)
            it.init(*args, **kwds)
            return it
        def init(self, *args, **kwds):
            pass

आप PEP 318 से इस कार्यान्वयन का उपयोग डेकोरेटर का उपयोग करके भी कर सकते हैं

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
...

1
लगता है initसे एक कमीने रूप कॉलिंग __new__वास्तव में हैकी है। यह वही है जो मेटाक्लास के लिए है।
मैड फिजिसिस्ट

6
class M(type):
    _dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print 'EXISTS'
            return cls._dict[key]
        else:
            print 'NEW'
            instance = super(M, cls).__call__(key)
            cls._dict[key] = instance
            return instance

class A(object):
    __metaclass__ = M

    def __init__(self, key):
        print 'INIT'
        self.key = key
        print

a1 = A('aaa')
a2 = A('bbb')
a3 = A('aaa')

आउटपुट:

NEW
INIT

NEW
INIT

EXISTS

एनबी एक साइड इफेक्ट M._dictसंपत्ति के रूप में स्वचालित Aरूप से सुलभ हो जाता है, A._dictइसलिए ध्यान रखें कि इसे संयोग से अधिलेखित न करें।


आप एक __init__विधि याद कर रहे हैं जो सेट करता है cls._dict = {}। आप शायद इस मेटाटाइप के सभी वर्गों (लेकिन विचार के लिए +1) द्वारा साझा किया गया शब्दकोश नहीं चाहते हैं।
मैड फिजिसिस्ट

5

__new__ को एक वर्ग का नया, रिक्त उदाहरण लौटाना चाहिए। __init__ को फिर उस उदाहरण को इनिशियलाइज़ करने के लिए कहा जाता है। आप __new__ के "NEW" मामले में __init__ को नहीं बुला रहे हैं, इसलिए इसे आपके लिए बुलाया जा रहा है। कोड जो कॉल कर रहा है, __new__वह इस बात पर नज़र नहीं रखता है कि __init__ को किसी विशेष उदाहरण पर बुलाया गया है या नहीं और न ही यह होना चाहिए, क्योंकि आप यहाँ कुछ बहुत ही असामान्य कर रहे हैं।

आप __init__ फ़ंक्शन में ऑब्जेक्ट के लिए एक विशेषता जोड़ सकते हैं यह इंगित करने के लिए कि यह आरंभिक है। __Init__ में पहली बात के रूप में उस विशेषता के अस्तित्व के लिए जाँच करें और यदि यह किया गया है तो आगे न बढ़ें।


5

@AntonyHatchkins उत्तर के लिए एक अपडेट, आप शायद मेटाटाइप के प्रत्येक वर्ग के लिए उदाहरणों का एक अलग शब्दकोश चाहते हैं, जिसका अर्थ है कि आपके पास __init__अपनी कक्षा की ऑब्जेक्ट को उस शब्दकोश के साथ आरंभ करने के बजाय मेटाक्लास में एक विधि होनी चाहिए, ताकि वह सभी वर्गों के लिए वैश्विक हो सके।

class MetaQuasiSingleton(type):
    def __init__(cls, name, bases, attibutes):
        cls._dict = {}

    def __call__(cls, key):
        if key in cls._dict:
            print('EXISTS')
            instance = cls._dict[key]
        else:
            print('NEW')
            instance = super().__call__(key)
            cls._dict[key] = instance
        return instance

class A(metaclass=MetaQuasiSingleton):
    def __init__(self, key):
        print 'INIT'
        self.key = key
        print()

मैंने आगे जाकर मूल कोड को एक __init__विधि के साथ अद्यतन किया और सिंटैक्स को पायथन 3 नोटेशन में बदल दिया (कॉल करने के लिए कोई-arg कॉल नहीं)super एक विशेषता के बजाय कक्षा के तर्कों में और मेटाक्लस नहीं)।

किसी भी तरह से, यहां महत्वपूर्ण बिंदु यह है कि आपकी कक्षा आरंभीक ( __call__विधि) __new__या __init__तो कुंजी निष्पादित होती है या नहीं । यह उपयोग करने की तुलना में बहुत अधिक स्वच्छ है __new__, जो आपको डिफ़ॉल्ट __init__चरण को छोड़ना चाहते हैं, तो आपको ऑब्जेक्ट को चिह्नित करना होगा ।


4

उस में थोड़ा गहरा खुदाई!

सीपीथॉन में एक सामान्य वर्ग का प्रकार है typeऔर इसका आधार वर्ग है Object(जब तक आप स्पष्ट रूप से एक मेटा बेस की तरह दूसरे आधार वर्ग को परिभाषित नहीं करते हैं)। निम्न स्तर की कॉल का क्रम यहां पाया जा सकता है । पहला तरीका जिसे type_callकॉल किया जाता है वह फिर कॉल करता है tp_newऔर फिरtp_init

यहां दिलचस्प बात tp_newयह है कि Object's (बेस क्लास) नई विधि को कॉल करेगा object_newजो कि tp_alloc( PyType_GenericAlloc) ऑब्जेक्ट के लिए मेमोरी आवंटित करता है :)

उस बिंदु पर ऑब्जेक्ट को मेमोरी में बनाया जाता है और फिर __init__विधि को बुलाया जाता है। अगर __init__आपकी कक्षा में इसे लागू नहीं किया object_initजाता है, तो इसे बुलाया जाता है और यह कुछ नहीं करता है :)

फिर type_callबस वह वस्तु लौटाता है जो आपके चर को बांधती है।


4

एक को __init__पारंपरिक OO भाषाओं में एक साधारण रचनाकार के रूप में देखना चाहिए । उदाहरण के लिए, यदि आप जावा या सी ++ से परिचित हैं, तो निर्माणकर्ता को अपने स्वयं के उदाहरण के लिए एक संकेतक पारित किया जाता है। जावा के मामले में, यह thisपरिवर्तनशील है। यदि कोई जावा के लिए उत्पन्न बाइट कोड का निरीक्षण करता है, तो दो कॉलों को नोटिस करेगा। पहली कॉल एक "नई" विधि के लिए है, और फिर अगली कॉल init विधि (जो उपयोगकर्ता परिभाषित कंस्ट्रक्टर के लिए वास्तविक कॉल है) के लिए है। यह दो चरण की प्रक्रिया कक्षा के कंस्ट्रक्टर विधि को कॉल करने से पहले वास्तविक उदाहरण के निर्माण में सक्षम बनाती है जो उस उदाहरण की सिर्फ एक अन्य विधि है।

अब, पायथन के मामले में, __new__एक अतिरिक्त सुविधा है जो उपयोगकर्ता के लिए सुलभ है। जावा उस लचीलेपन को प्रदान नहीं करता है, इसकी टाइप की हुई प्रकृति के कारण। यदि कोई भाषा उस सुविधा को प्रदान करती है, तो __new__कुछ मामलों में एक असंबंधित वस्तु का पूरी तरह से नया उदाहरण बनाने सहित, वापस लौटने से पहले उस तरीके से कई कार्य कर सकता है। और, यह दृष्टिकोण विशेष रूप से पायथन के मामले में अपरिवर्तनीय प्रकारों के लिए भी अच्छा काम करता है।


4

हालांकि, मैं थोड़ा उलझन में हूं कि __init__हमेशा बाद में क्यों बुलाया जाता है __new__

मुझे लगता है कि C ++ सादृश्य यहाँ उपयोगी होगा:

  1. __new__बस वस्तु के लिए स्मृति आवंटित करता है। किसी ऑब्जेक्ट के इंस्टेंस वेरिएबल्स को इसे होल्ड करने के लिए मेमोरी की जरूरत होती है, और यही वह कदम __new__है।

  2. __init__ ऑब्जेक्ट के आंतरिक चर को विशिष्ट मानों के लिए प्रारंभ करें (डिफ़ॉल्ट हो सकता है)।


2

के __init__बाद कहा जाता है __new__ताकि जब आप इसे एक उपवर्ग में ओवरराइड करते हैं, तो आपका जोड़ा गया कोड अभी भी कहा जाएगा।

यदि आप एक ऐसे वर्ग को अपने अधीन करने की कोशिश कर रहे हैं __new__, जो पहले से ही एक है , तो इस बारे में अनभिज्ञ कोई व्यक्ति शुरू कर सकता है __init__और कॉल को उपवर्ग में भेज सकता है __init__। के __init__बाद बुला के इस सम्मेलन__new__ उम्मीद के मुताबिक काम मदद करता है।

__init__अभी तक किसी भी पैरामीटर सुपर क्लास के लिए अनुमति देने के लिए की जरूरत है __new__की जरूरत है, लेकिन ऐसा करने में नाकाम रहने के आम तौर पर एक स्पष्ट रनटाइम त्रुटि पैदा करेगा। और __new__शायद स्पष्ट रूप से अनुमति देने के लिए *argsऔर '** kw' के लिए अनुमति देनी चाहिए , यह स्पष्ट करने के लिए कि एक्सटेंशन ठीक है।

यह आम तौर पर बुरा प्रपत्र दोनों के लिए है __new__और __init__व्यवहार मूल पोस्टर वर्णित की वजह से विरासत का एक ही स्तर पर एक ही कक्षा में,।


1

हालांकि, मैं थोड़ा उलझन में हूं कि __init__हमेशा बाद में क्यों बुलाया जाता है __new__

इसके अलावा एक कारण नहीं है कि यह सिर्फ इस तरह से किया जाता है। __new__कक्षा को शुरू करने की जिम्मेदारी नहीं है, कुछ अन्य विधि ( __call__, संभवतः - मुझे निश्चित रूप से नहीं पता है)।

मुझे इसकी उम्मीद नहीं थी। क्या कोई मुझे बता सकता है कि ऐसा क्यों हो रहा है और मैं इस कार्यक्षमता को कैसे लागू करूं? (कार्यान्वयन को अलग रखने के अलावा, __new__जो काफी हैकरी लगता है)।

यदि आप __init__पहले से ही इसे आरंभ कर चुके हैं, तो आप कुछ भी नहीं कर सकते हैं , या आप एक नए के साथ एक नया मेटाक्लास लिख सकते हैं __call__जो केवल __init__नए उदाहरणों पर कॉल करता है, और अन्यथा केवल रिटर्न करता है __new__(...)


1

सरल कारण यह है कि नए का उपयोग एक इंस्टेंस बनाने के लिए किया जाता है, जबकि इनिट का उपयोग इंस्टेंस को इनिशियलाइज़ करने के लिए किया जाता है। प्रारंभिक करने से पहले, उदाहरण पहले बनाया जाना चाहिए। इसलिए नए को इनिट से पहले बुलाया जाना चाहिए ।


1

अब मुझे वही समस्या मिली है, और कुछ कारणों से मैंने सज्जाकार, कारखानों और मेटाक्लासेस से बचने का फैसला किया है। मैंने इसे इस तरह किया:

मुख्य फ़ाइल

def _alt(func):
    import functools
    @functools.wraps(func)
    def init(self, *p, **k):
        if hasattr(self, "parent_initialized"):
            return
        else:
            self.parent_initialized = True
            func(self, *p, **k)

    return init


class Parent:
    # Empty dictionary, shouldn't ever be filled with anything else
    parent_cache = {}

    def __new__(cls, n, *args, **kwargs):

        # Checks if object with this ID (n) has been created
        if n in cls.parent_cache:

            # It was, return it
            return cls.parent_cache[n]

        else:

            # Check if it was modified by this function
            if not hasattr(cls, "parent_modified"):
                # Add the attribute
                cls.parent_modified = True
                cls.parent_cache = {}

                # Apply it
                cls.__init__ = _alt(cls.__init__)

            # Get the instance
            obj = super().__new__(cls)

            # Push it to cache
            cls.parent_cache[n] = obj

            # Return it
            return obj

उदाहरण वर्ग

class A(Parent):

    def __init__(self, n):
        print("A.__init__", n)


class B(Parent):

    def __init__(self, n):
        print("B.__init__", n)

उपयोग में

>>> A(1)
A.__init__ 1  # First A(1) initialized 
<__main__.A object at 0x000001A73A4A2E48>
>>> A(1)      # Returned previous A(1)
<__main__.A object at 0x000001A73A4A2E48>
>>> A(2)
A.__init__ 2  # First A(2) initialized
<__main__.A object at 0x000001A7395D9C88>
>>> B(2)
B.__init__ 2  # B class doesn't collide with A, thanks to separate cache
<__main__.B object at 0x000001A73951B080>
  • चेतावनी: आप प्रारंभ नहीं जनक है, यह चाहिए होगा अन्य वर्गों के साथ टकराने - जब तक आप बच्चों में से प्रत्येक में अलग कैश परिभाषित, है कि हम क्या नहीं करना चाहता।
  • चेतावनी: यह माता-पिता के साथ एक वर्ग लगता है क्योंकि दादा-दादी अजीब व्यवहार करते हैं। [असत्यापित]

इसे ऑनलाइन आज़माएं!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.