2011-06-17 22 views
41

क्या अजगर में कक्षा के निर्माता __init__ को बाधित करने का कोई तरीका है?क्या __init__ को कॉल किए बिना कक्षा को तुरंत चालू करने का कोई तरीका है?

उदाहरण:

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

    def Print(self): 
     print "YEHAA" 

अब मैं A का एक उदाहरण बनाने के लिए करना चाहते हैं। यह ऐसा दिखाई दे सकता है, हालांकि यह वाक्यविन्यास सही नहीं है।

a = A 
a.Print() 

संपादित करें:

एक और भी जटिल उदाहरण:

मान लीजिए मैं एक वस्तु C, जो उद्देश्य यह एक एकल पैरामीटर की दुकान है और इसके साथ कुछ संगणना करने के लिए है। पैरामीटर, हालांकि, इस तरह से पारित नहीं किया गया है, लेकिन यह एक विशाल पैरामीटर फ़ाइल में एम्बेडेड है। यह कुछ इस तरह दिखाई दे सकता है:

class C(object): 
    def __init__(self, ParameterFile): 
     self._Parameter = self._ExtractParamterFile(ParameterFile) 
    def _ExtractParamterFile(self, ParameterFile): 
     #does some complex magic to extract the right parameter 
     return the_extracted_parameter 

अब मैं डंप और उस वस्तु C का एक उदाहरण लोड करने के लिए करना चाहते हैं। हालांकि, जब मैं इस ऑब्जेक्ट को लोड करता हूं, तो मेरे पास केवल एक परिवर्तनीय self._Parameter है और मैं कन्स्ट्रक्टर को कॉल नहीं कर सकता, क्योंकि यह पैरामीटर फ़ाइल की अपेक्षा कर रहा है।

@staticmethod 
    def Load(file): 
     f = open(file, "rb") 
     oldObject = pickle.load(f) 
     f.close() 

     #somehow create newObject without calling __init__ 
     newObject._Parameter = oldObject._Parameter 
     return newObject 

दूसरे शब्दों में, पैरामीटर फ़ाइल को पास किए बिना एक उदाहरण बनाना संभव नहीं है। मेरे "असली" मामले में, हालांकि, यह पैरामीटर फ़ाइल नहीं है लेकिन डेटा का कुछ बड़ा हिस्सा मैं निश्चित रूप से स्मृति में नहीं लेना चाहता हूं या इसे डिस्क पर भी स्टोर नहीं करना चाहता हूं।

और चूंकि मैं Load विधि से C का उदाहरण वापस लौटना चाहता हूं, तो मुझे किसी भी तरह से कन्स्ट्रक्टर को कॉल करना होगा।

पुराने संपादित करें:

एक अधिक जटिल उदाहरण है, जो बताते हैं क्यों मैं सवाल पूछ रहा हूँ:,

class B(object):  
    def __init__(self, name, data): 
     self._Name = name 
     #do something with data, but do NOT save data in a variable 

    @staticmethod 
    def Load(self, file, newName): 
     f = open(file, "rb") 
     s = pickle.load(f) 
     f.close() 

     newS = B(???) 
     newS._Name = newName 
     return newS 

आप देख सकते हैं data के बाद से एक वर्ग चर मैं नहीं कर सकता में संग्रहीत नहीं है इसे __init__ पर पास करें। बेशक मैं इसे आसानी से स्टोर कर सकता हूं, लेकिन क्या होगा यदि डेटा एक बड़ी वस्तु है, जिसे मैं हर समय स्मृति में ले जाना नहीं चाहता हूं या इसे डिस्क पर भी सहेजना नहीं चाहता हूं?

+5

के तहत चल रहा है आप इसे किस अंत में करना चाहते हैं? –

+2

अजीब सवाल .... वास्तव में क्या करने की कोशिश कर रहे हैं? –

+0

मेरे पास एक "अनपिक्षित" ऑब्जेक्ट है जिसमें से मैं एक ही प्रकार की एक नई वस्तु बनाना चाहता हूं।हालांकि, इस अप्रचलित वस्तु से मेरे पास __init__ को खिलाने के लिए पर्याप्त डेटा नहीं है। सी ++ में मैं कन्स्ट्रक्टर को अधिभारित कर दूंगा और इसे निजी बनाउंगा, पायथन में मैं ^^ खो गया हूं। __init__ में वर्तमान पैरामीटर हालांकि सही समझ में आता है। यह मेरी दुर्दशा है। – Woltan

उत्तर

43

पर कॉल करके __init__ को बाधित कर सकते हैं। फिर आप दिए गए प्रकार का ऑब्जेक्ट बना सकते हैं और __init__ के लिए वैकल्पिक विधि कॉल कर सकते हैं। यह ऐसा कुछ है जो pickle करेगा।

हालांकि, पहली मैं बहुत ज्यादा है कि यह कुछ है कि आप ऐसा नहीं करना चाहिए और जो भी आप प्राप्त करना चाहते हैं पर जोर देना चाहते हैं, वहाँ यह करने के लिए बेहतर तरीके, जिनमें से कुछ कर रहे हैं अन्य उत्तरों में उल्लेख किया गया है। विशेष रूप से, यह खराब कॉलिंग __init__ को छोड़ने का विचार है।

जब ऑब्जेक्ट का निर्माण होगा, कम या ज्यादा होता है:

a = A.__new__(A, *args, **kwargs) 
a.__init__(*args, **kwargs) 

आप दूसरे चरण को छोड़ सकता है।

यहाँ आप ऐसा क्यों नहीं चाहिए:__init__ के प्रयोजन वस्तु को प्रारंभ, सभी क्षेत्रों में भरें और सुनिश्चित करें कि माता-पिता वर्गों के __init__ तरीकों में भी कहा जाता है के लिए है।pickle के साथ यह एक अपवाद है क्योंकि यह ऑब्जेक्ट से जुड़े सभी डेटा को संग्रहीत करने का प्रयास करता है (किसी भी फ़ील्ड/ऑब्जेक्ट वेरिएबल्स जो ऑब्जेक्ट के लिए सेट हैं), और इसलिए पिछली बार __init__ द्वारा सेट की गई कुछ भी पुनर्स्थापित की जाएगी अचार, इसे फिर से कॉल करने की कोई आवश्यकता नहीं है।

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

इसके अलावा ऐसा करने से आप पाइथन के उदाहरण के निर्माण को कम करने और अपना खुद का निर्माण करने के लिए कम या ज्यादा कम करेंगे। पाइथन पहले से ही आपके लिए बहुत अच्छा करता है, इसे पुनर्निर्मित करने की कोई आवश्यकता नहीं है और यह आपके कोड का उपयोग करने वाले लोगों को भ्रमित कर देगा।

यहाँ के लिए क्या सबसे अच्छा बजाय कैसे करते है: एक भी __init__ विधि वर्ग है कि सभी उदाहरण चर ठीक से आरंभ के सभी संभव instantiations के लिए कहा जा रहा है कि का उपयोग करें। __init__ कि वैकल्पिक तर्क का उपयोग करके अपने मामलों को संभालने के लिए

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

हैं __init__ एक वैकल्पिक चरण है (उदाहरण के लिए प्रसंस्करण कि data तर्क है, हालांकि आप अधिक विशिष्ट होगा की तरह), आप इसे एक वैकल्पिक तर्क या तो या एक सामान्य विधि है कि संसाधन करता है कर सकते हैं .. । अथवा दोनों।

+1

कोई भी 'ए = ऑब्जेक्ट .__ नया __ (ए)' का उपयोग कर सकता है, यदि आप एक अतिव्यापी 'ए .__ new__' नहीं कहना चाहते हैं। – kay

+3

इसके लिए वैध उपयोग यह है कि यदि आप एक कारखाना पैटर्न बना रहे हैं और किसी अन्य वर्ग में निर्माण का प्रतिनिधि बनाना चाहते हैं, लेकिन इस मामले में डिफ़ॉल्ट निर्माण करना चाहते हैं कि आप कक्षा के लिए कारखाने का उपयोग न करें। 'कक्षा थ्रेडपूल फैक्टरी: डीफ़ पर्सिस्टेंट थ्रेडपूल(): थ्रेडपूल .__ new__ .... इसे बनाएं और लॉग करें ' – gbtimmon

2

जैसा कि मैंने मेरी टिप्पणी में कहा है कि आप अपने __init__ विधि को बदल सकता है ताकि वह अपने मापदंडों के किसी भी मान दिए बिना निर्माण की अनुमति देता:

def __init__(self, p0, p1, p2): 
    # some logic 

बन जाएगा:

def __init__(self, p0=None, p1=None, p2=None): 
    if p0 and p1 and p2: 
     # some logic 

या:

def __init__(self, p0=None, p1=None, p2=None, init=True): 
    if init: 
     # some logic 
+0

हाँ, मैं पैरामीटर डिफ़ॉल्ट कर सकता हूं, लेकिन यह वास्तव में मेरे प्रश्न का उत्तर नहीं देता है। लेकिन शायद आप सही हैं, और मेरे वर्ग में मेरे पास कुछ प्रकार की लेआउट समस्या है। – Woltan

7

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

def emptyinit(self): 
    pass 
A.__init__ = emptyinit 
a = A() 
a.Print() 

इस गतिशील रूप से बाहर जो __init__ विधि वर्ग से, स्विच करेंगे एक खाली कॉल और इसकी जगह। ध्यान दें कि यह संभवतः करने के लिए एक अच्छी बात नहीं है, क्योंकि यह सुपर क्लास की __init__ विधि को कॉल नहीं करती है।

तुम भी __init__ विधि अधिभावी क्या आप इसे करने के (शायद कुछ भी नहीं) करना चाहते हैं को छोड़ कर अपने ही वर्ग है कि सब कुछ एक ही करता है बनाने के लिए इसका उपवर्ग सकता है।

शायद, हालांकि, आप बस ऑब्जेक्ट को तत्काल किए बिना कक्षा से विधि को कॉल करना चाहते हैं। यदि ऐसा है, तो आपको @classmethod और @staticmethod सजावट में देखना चाहिए। वे सिर्फ उस तरह के व्यवहार की अनुमति देते हैं।

अपने कोड में आपने @staticmethod सजावट डाली है, जो self तर्क नहीं लेती है। शायद क्या प्रयोजन के लिए बेहतर हो सकता है जाएगा एक @classmethod, यह कुछ ऐसा दिखाई हो सकता है:

@classmethod 
def Load(cls, file, newName): 
    # Get the data 
    data = getdata() 
    # Create an instance of B with the data 
    return cls.B(newName, data) 

अद्यतन: रोश के बहुत बढ़िया जवाब ने बताया कि आप __new__ को लागू है, जो मैं वास्तव में पता नहीं था द्वारा __init__ बुला से बच सकते हैं (हालांकि यह सही समझ में आता है)। धन्यवाद रोश! अपने Load विधि के लिए

14

उपयोग classmethod डेकोरेटर:

class B(object):  
    def __init__(self, name, data): 
     self._Name = name 
     #store data 

    @classmethod 
    def Load(cls, file, newName): 
     f = open(file, "rb") 
     s = pickle.load(f) 
     f.close() 

     return cls(newName, s) 

तो तुम कर सकते हैं:

loaded_obj = B.Load('filename.txt', 'foo') 

संपादित करें:

वैसे भी, अगर आप अभी भी __init__ विधि छोड़ करना चाहते हैं, __new__ कोशिश :

>>> class A(object): 
...  def __init__(self): 
...    print '__init__' 
... 
>>> A() 
__init__ 
<__main__.A object at 0x800f1f710> 
>>> a = A.__new__(A) 
>>> a 
<__main__.A object at 0x800f1fd50> 
+0

यह अच्छी चीजें है। +1 – motoku

+0

इस मामले में 'रिटर्न सीएलएस (न्यूनाम, एस)' में, 'डेटा'' '' होगा। लेकिन यह मामला नहीं है, क्योंकि मुझे नहीं पता कि 'डेटा' क्या है। – Woltan

+1

@ वोल्टन मैं भी नहीं जानता। शायद आपको एक बार फिर अपना प्रश्न विस्तारित करना चाहिए? –

9

सचमुच अपने प्रश्न उठाते हुए मैं मेटा वर्गों का प्रयोग करेंगे:

class MetaSkipInit(type): 
    def __call__(cls): 
     return cls.__new__(cls) 


class B(object): 
    __metaclass__ = MetaSkipInit 

    def __init__(self): 
     print "FAILURE" 

    def Print(self): 
     print "YEHAA" 


b = B() 
b.Print() 

यह उपयोगी हो सकता है जैसे पैरामीटर सूची प्रदूषित किए बिना कन्स्ट्रक्टर की प्रतिलिपि बनाने के लिए। लेकिन ऐसा करने के लिए मेरे प्रस्तावित हैक से अधिक काम और देखभाल होगी।

+0

यह एक अच्छा समाधान है कि एक कस्टम' __call__' लागू करके डिफ़ॉल्ट '__new__' +' __init__' को ओवरराइड करने का तरीका है। मेटाक्लास स्तर पर विधि। यह बिना किसी 'हैकिंग' के करता है और आपको अन्य उपयोगी सामान (जैसे फ्लाईवेट पैटर्न को कार्यान्वित करने) की अनुमति देता है और उपयोगकर्ता से पूरी तरह छुपा रहता है। उपयोगकर्ता के लिए यह अभी भी एक सामान्य कन्स्ट्रक्टर कॉल की तरह दिखता है। – dieterg

5

मैं अजगर रसोई की किताब पढ़ रहा था और वहाँ एक अनुभाग इस बारे में बात कर रहा है: उदाहरण के __init__()

 
>>> class A: 
    def __init__(self,a): 
     self.a = a 


>>> test = A('a') 
>>> test.a 
'a' 
>>> test_noinit = A.__new__(A) 
>>> test_noinit.a 
Traceback (most recent call last): 
    File "", line 1, in 
    test_noinit.a 
AttributeError: 'A' object has no attribute 'a' 
>>> 

बायपास करने के लिए हालांकि मुझे लगता है कि python3 में यह केवल काम करता है __new__ का उपयोग कर दिया जाता है। नीचे 2.7

 
>>> class A: 
    def __init__(self,a): 
     self.a = a 


>>> test = A.__new__(A) 

Traceback (most recent call last): 
    File "", line 1, in 
    test = A.__new__(A) 
AttributeError: class A has no attribute '__new__' 
>>> 
+1

2.7 के साथ आपको "नई स्टाइल क्लास" का उपयोग करने की आवश्यकता है - यानी, सबक्लास ऑब्जेक्ट: 'क्लास ए (ऑब्जेक्ट)'। –

संबंधित मुद्दे

 संबंधित मुद्दे