पायथन (और पायथन सी एपीआई): __new__ बनाम __init__


126

जो सवाल मैं पूछने जा रहा हूँ , वह __new__ और __init__ के पायथन के उपयोग की नकल प्रतीत होता है ? है, लेकिन परवाह किए बिना, यह अभी स्पष्ट नहीं मेरे लिए वास्तव में क्या के बीच व्यावहारिक अंतर है __new__और __init__है।

इससे पहले कि आप मुझे यह बताने के लिए दौड़ें कि __new__ऑब्जेक्ट बनाने के लिए है और ऑब्जेक्ट __init__को इनिशियलाइज़ करने के लिए है, मुझे स्पष्ट करने दें: मुझे वह मिल गया है। वास्तव में, वह अंतर मेरे लिए काफी स्वाभाविक है, क्योंकि मेरे पास C ++ में अनुभव है, जहां हमारे पास प्लेसमेंट नया है , जो ऑब्जेक्ट आवंटन को प्रारंभ से अलग करता है।

अजगर सी एपीआई ट्यूटोरियल इस तरह यह बताते हैं:

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

तो, हाँ - मुझे लगता है कि क्या __new__करता है, लेकिन इसके बावजूद, मुझे अभी भी समझ नहीं आया कि यह पायथन में क्यों उपयोगी है। दिए गए उदाहरण में कहा गया है कि __new__यह उपयोगी हो सकता है यदि आप "उदाहरण चर के प्रारंभिक मूल्यों को आश्वस्त करना चाहते हैं"। खैर, क्या __init__ऐसा नहीं है?

सी एपीआई ट्यूटोरियल में, एक उदाहरण दिखाया गया है जहां एक नया प्रकार (जिसे "नोडी" कहा जाता है) बनाया गया है, और प्रकार का __new__फ़ंक्शन परिभाषित किया गया है। Noddy प्रकार में एक स्ट्रिंग सदस्य होता है जिसे कॉल किया जाता है first, और यह स्ट्रिंग सदस्य किसी रिक्त स्ट्रिंग के लिए आरंभिक होता है जैसे:

static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    .....

    self->first = PyString_FromString("");
    if (self->first == NULL)
    {
       Py_DECREF(self);
       return NULL;
    }

    .....
}

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

जवाबों:


137

अंतर मुख्य रूप से उत्परिवर्ती बनाम अपरिवर्तनीय प्रकारों के साथ उत्पन्न होता है।

__new__एक प्रकार को पहले तर्क के रूप में स्वीकार करता है , और (आमतौर पर) उस प्रकार का एक नया उदाहरण देता है। इस प्रकार यह परिवर्तनशील और अपरिवर्तनीय दोनों प्रकार के उपयोग के लिए उपयुक्त है।

__init__एक उदाहरण को पहले तर्क के रूप में स्वीकार करता है और उस उदाहरण की विशेषताओं को संशोधित करता है। यह एक अपरिवर्तनीय प्रकार के लिए अनुचित है, क्योंकि यह उन्हें कॉल करके निर्माण के बाद संशोधित करने की अनुमति देगा obj.__init__(*args)

के व्यवहार की तुलना करें tupleऔर list:

>>> x = (1, 2)
>>> x
(1, 2)
>>> x.__init__([3, 4])
>>> x # tuple.__init__ does nothing
(1, 2)
>>> y = [1, 2]
>>> y
[1, 2]
>>> y.__init__([3, 4])
>>> y # list.__init__ reinitialises the object
[3, 4]

जैसा कि वे अलग क्यों हैं (सरल ऐतिहासिक कारणों से अलग): __new__विधियों को सही (प्रारंभिक वस्तु निर्माण, और फिर अंत में वस्तु को वापस करने के लिए याद रखना) के लिए बॉयलरप्लेट के एक गुच्छा की आवश्यकता होती है। __init__तरीकों, इसके विपरीत, मृत सरल हैं, क्योंकि आप बस जो भी विशेषताओं को सेट करने की आवश्यकता है, सेट करते हैं।

__init__लिखने के लिए आसान होने के तरीकों के अलावा , और ऊपर उल्लिखित उत्परिवर्ती बनाम अपरिवर्तनीय अंतर, __init__किसी भी बिल्कुल आवश्यक इंस्ट्रूमेंट इनवेरिएंट्स की स्थापना करके उपवर्गों में अभिभावक वर्ग को कॉल करने के लिए अलगाव का फायदा उठाया जा सकता है __new__। यह आम तौर पर एक संदिग्ध अभ्यास है - यह आमतौर पर केवल मूल वर्ग __init__विधियों को आवश्यक रूप से कॉल करने के लिए स्पष्ट है।


1
कोड जिसे आप "बॉयलरप्लेट" कहते हैं, __new__वह बॉयलरप्लेट नहीं है, क्योंकि बॉयलरप्लेट कभी नहीं बदलता है। कभी-कभी आपको उस विशेष कोड को कुछ अलग करने की आवश्यकता होती है।
माइल्स रुट

13
बनाना, या अन्यथा प्राप्त करना, उदाहरण (आमतौर पर एक superकॉल के साथ ) और आवृत्ति वापस करना किसी भी __new__कार्यान्वयन के आवश्यक भाग हैं , और "बॉयलरप्लेट" जिसका मैं उल्लेख कर रहा हूं। इसके विपरीत, के passलिए एक वैध कार्यान्वयन है __init__- जो भी आवश्यक व्यवहार नहीं है।
ncoghlan

37

वहाँ शायद के लिए अन्य उपयोग कर रहे हैं, __new__लेकिन वहाँ एक बहुत स्पष्ट एक है: आप का उपयोग किए बिना एक अपरिवर्तनीय प्रकार उपवर्ग नहीं कर सकते __new__। उदाहरण के लिए, मान लीजिए कि आप टपल का एक उपवर्ग बनाना चाहते हैं जिसमें केवल 0 और के बीच अभिन्न मूल्य हो सकते हैं size

class ModularTuple(tuple):
    def __new__(cls, tup, size=100):
        tup = (int(x) % size for x in tup)
        return super(ModularTuple, cls).__new__(cls, tup)

आप बस के साथ ऐसा नहीं कर सकते __init__हैं - यदि आप संशोधित करने का प्रयास selfमें __init__, दुभाषिया शिकायत करेगा कि आप एक अपरिवर्तनीय वस्तु को संशोधित करने की कोशिश कर रहे हैं।


1
मुझे समझ नहीं आता कि हमें सुपर का उपयोग क्यों करना चाहिए? मेरा मतलब है कि नए को सुपरक्लास का एक उदाहरण क्यों देना चाहिए ? इसके अलावा, जैसा कि आपका कहना है, हमें नए सिरे से क्लॉस पास क्यों करना चाहिए ? सुपर (ModularTuple, cls) एक बाध्य विधि नहीं लौटाता है?
Alcott

3
@ अलॉट, मुझे लगता है कि आप के व्यवहार को गलत समझ रहे हैं __new__। हम clsस्पष्ट रूप से पास करते हैं __new__, क्योंकि आप यहाँ पढ़ सकते हैं, __new__ हमेशा इसके पहले तर्क के रूप में एक प्रकार की आवश्यकता होती है। यह उस प्रकार का एक उदाहरण देता है। इसलिए हम सुपरक्लास का एक उदाहरण नहीं लौटा रहे हैं - हम एक उदाहरण लौटा रहे हैं cls। इस मामले में, यह वैसा ही है जैसा हमने कहा था tuple.__new__(ModularTuple, tup)
प्रेषिती

35

__new__()यह जिस वर्ग से जुड़ा है, उसके अलावा अन्य प्रकार की वस्तुओं को वापस कर सकता है। __init__()केवल कक्षा के एक मौजूदा उदाहरण को इनिशियलाइज़ करता है।

>>> class C(object):
...   def __new__(cls):
...     return 5
...
>>> c = C()
>>> print type(c)
<type 'int'>
>>> print c
5

यह अब तक की सबसे स्पष्ट व्याख्या है।
तारिक

बिल्कुल सच नहीं है। मेरे पास __init__तरीके हैं जिनमें कोड है जो दिखता है self.__class__ = type(...)। यह उस वस्तु का कारण बनता है जिसे आपने सोचा था कि आप एक अलग वर्ग के हैं। मैं वास्तव में इसे बदल नहीं सकता intजैसा कि आपने किया था ... मुझे ढेर प्रकारों या कुछ के बारे में एक त्रुटि मिलती है ... लेकिन इसे गतिशील रूप से बनाए गए कक्षा कार्यों में असाइन करने का मेरा उदाहरण है।
आर्टऑफवर्फ

मैं भी, जब __init__()कहा जाता है के बारे में उलझन में हूँ । उदाहरण के लिए, में lonetwin का जवाब है, या तो Triangle.__init__()या Square.__init__()स्वचालित रूप से जो प्रकार के आधार पर कहा जाता हो __new__()रिटर्न। आप अपने उत्तर में क्या कहते हैं (और मैंने इसे कहीं और पढ़ा है), ऐसा प्रतीत नहीं होता है कि दोनों में से कोई भी Shape.__new__() ऐसा नहीं होना चाहिए, जो न तो इसका उदाहरण दे रहा है cls(न ही इसका एक उपवर्ग)।
मार्टीन्यू

1
@martineau: __init__()लॉनेटविन के जवाब में तरीकों को बुलाया जाता है जब व्यक्तिगत वस्तुओं को त्वरित रूप से प्राप्त किया जाता है (यानी जब उनकी __new__() विधि वापस आती है), न कि जब Shape.__new__()रिटर्न।
इग्नासियो वाज़केज़-अब्राम्स

आह, ठीक है, Shape.__init__()(अगर यह एक था) नहीं बुलाया जाएगा। अब यह सब और अधिक समझ में आ रहा है ...:¬)
मार्टीन्यू

13

पूरा जवाब नहीं लेकिन शायद कुछ ऐसा है जो अंतर दिखाता है।

__new__हमेशा कहा जाता है जब एक वस्तु का निर्माण करना होगा। कुछ स्थितियां ऐसी हैं, जहां __init__बुलाया नहीं जाएगा। एक उदाहरण यह है कि जब आप अचार फ़ाइल से वस्तुओं को अनपिक करते हैं, तो उन्हें आबंटित ( __new__) किया जाएगा, लेकिन आरंभिक ( __init__) नहीं।


यदि मैं स्मृति को आबंटित करना चाहता हूं और आरंभिक होने के लिए डेटा चाहता हूं तो क्या मैं नए से इनिट कहूंगा ? अगर init बनाने के दौरान नया मौजूद नहीं है, तो इसे कॉल क्यों किया जाता है?
redpix_

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

3

बस बनाम परिभाषित करने के इरादे (व्यवहार के विपरीत) के बारे में एक शब्द जोड़ना चाहते हैं ।__new____init__

मैं इस सवाल (अन्य लोगों के बीच) में आया जब मैं एक वर्ग कारखाने को परिभाषित करने का सबसे अच्छा तरीका समझने की कोशिश कर रहा था। मैंने महसूस किया कि इन तरीकों में से __new__एक वैचारिक रूप से अलग __init__है, यह तथ्य यह है कि इसका लाभ __new__वही है जो प्रश्न में कहा गया था:

तो __new__ पद्धति का एकमात्र लाभ यह है कि उदाहरण चर खाली स्ट्रिंग के रूप में शुरू होगा, जैसा कि NULL के विपरीत है। लेकिन यह कभी क्यों उपयोगी है, क्योंकि अगर हम यह सुनिश्चित करने के बारे में परवाह करते हैं कि हमारे उदाहरण चर कुछ डिफ़ॉल्ट मान के साथ आरंभ किए जाते हैं, तो हम बस __in____ विधि में ही कर सकते हैं?

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

मैं इस बारे में तब तक उलझन में था जब तक कि मैं वास्तव में इसके अर्थ के बजाय अवधारणा के अनुप्रयोग के बारे में नहीं सोचता। यहाँ एक उदाहरण है जो उम्मीद है कि अंतर स्पष्ट कर देगा:

a = Shape(sides=3, base=2, height=12)
b = Shape(sides=4, length=2)
print(a.area())
print(b.area())

# I want `a` and `b` to be an instances of either of 'Square' or 'Triangle'
# depending on number of sides and also the `.area()` method to do the right
# thing. How do I do that without creating a Shape class with all the
# methods having a bunch of `if`s ? Here is one possibility

class Shape:
    def __new__(cls, sides, *args, **kwargs):
        if sides == 3:
            return Triangle(*args, **kwargs)
        else:
            return Square(*args, **kwargs)

class Triangle:
    def __init__(self, base, height):
        self.base = base
        self.height = height

    def area(self):
        return (self.base * self.height) / 2

class Square:
    def __init__(self, length):
        self.length = length

    def area(self):
        return self.length*self.length

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

यदि आप एक नियमित वर्ग (उर्फ एक गैर-मेटाक्लस) बना रहे हैं, तो __new__वास्तव में कोई मतलब नहीं है जब तक कि यह विशेष मामला नहीं है जैसे ncoghlan के उत्तर के उत्तर में परिवर्तनशील बनाम अपरिवर्तनीय परिदृश्य (जो कि परिभाषित करने की अवधारणा का अनिवार्य रूप से अधिक विशिष्ट उदाहरण है) वर्ग / प्रकार के प्रारंभिक मूल्यों / गुणों के माध्यम __new__से बनाया जा रहा है (तब के माध्यम से initialized __init__)।

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