2009-05-15 17 views
36

मैंने हाल ही में पाइथन में एक बग से जूझ लिया है। यह उन मूर्ख नौसिखियों की बगों में से एक था, लेकिन मुझे पाइथन के तंत्र के बारे में सोचने लगा (मैं लंबे समय से सी ++ प्रोग्रामर हूं, पाइथन के लिए नया)। मैं बग्गी कोड डालूंगा और समझाऊंगा कि मैंने इसे ठीक करने के लिए क्या किया है, और उसके बाद मेरे पास कुछ प्रश्न हैं ...पायथन क्लास के सदस्यों की शुरुआत

परिदृश्य: मेरे पास ए नामक एक वर्ग है, जिसका एक शब्दकोश डेटा सदस्य है, निम्नलिखित है अपने कोड (इस पाठ्यक्रम का सरलीकरण है):

class A: 
    dict1={} 

    def add_stuff_to_1(self, k, v): 
     self.dict1[k]=v 

    def print_stuff(self): 
     print(self.dict1) 

वर्ग इस कोड का उपयोग वर्ग बी है:

class B: 

    def do_something_with_a1(self): 
     a_instance = A() 
     a_instance.print_stuff()   
     a_instance.add_stuff_to_1('a', 1) 
     a_instance.add_stuff_to_1('b', 2)  
     a_instance.print_stuff() 

    def do_something_with_a2(self): 
     a_instance = A()  
     a_instance.print_stuff()    
     a_instance.add_stuff_to_1('c', 1) 
     a_instance.add_stuff_to_1('d', 2)  
     a_instance.print_stuff() 

    def do_something_with_a3(self): 
     a_instance = A()  
     a_instance.print_stuff()    
     a_instance.add_stuff_to_1('e', 1) 
     a_instance.add_stuff_to_1('f', 2)  
     a_instance.print_stuff() 

    def __init__(self): 
     self.do_something_with_a1() 
     print("---") 
     self.do_something_with_a2() 
     print("---") 
     self.do_something_with_a3() 

सूचना है कि do_something_with_aX() के लिए हर कॉल वर्ग एक का एक नया "क्लीन" उदाहरण initializes, और जोड़ के पहले और बाद में शब्दकोश को प्रिंट करता है।

बग (मामले में आप अभी तक बाहर यह समझ नहीं किया है):

>>> b_instance = B() 
{} 
{'a': 1, 'b': 2} 
--- 
{'a': 1, 'b': 2} 
{'a': 1, 'c': 1, 'b': 2, 'd': 2} 
--- 
{'a': 1, 'c': 1, 'b': 2, 'd': 2} 
{'a': 1, 'c': 1, 'b': 2, 'e': 1, 'd': 2, 'f': 2} 

क्लास ए की दूसरी प्रारंभ में, शब्दकोशों खाली नहीं हैं, लेकिन पिछले प्रारंभ की सामग्री के साथ शुरू करते हैं, इत्यादि। मुझे उम्मीद थी कि वे "ताजा" शुरू करें।

क्या इस "बग" हल करती है स्पष्ट रूप से जोड़ रहा है:

self.dict1 = {} 

वर्ग ए की __init__ निर्माता हालांकि, कि मुझे आश्चर्य बनाया में:

  1. क्या "dict1 का अर्थ है = {} "dict1 की घोषणा के बिंदु पर प्रारंभिकरण (कक्षा ए में पहली पंक्ति)? यह व्यर्थ है?
  2. तत्कालता की व्यवस्था क्या है जो संदर्भ को अंतिम प्रारंभ से कॉपी करने का कारण बनती है?
  3. यदि मैं निर्माता (या किसी अन्य डेटा सदस्य) में "self.dict1 = {}" जोड़ता हूं, तो यह पहले प्रारंभिक उदाहरणों के शब्दकोश सदस्य को कैसे प्रभावित नहीं करता है?

संपादित करें: जवाब अब मैं समझता हूँ के बाद कि एक डेटा सदस्य घोषित करने और __init__ में यह जिक्र नहीं या कहीं और self.dict1 के रूप में, मैं व्यावहारिक रूप से परिभाषित करने कर रहा हूँ क्या सी ++/जावा में कहा जाता है के द्वारा एक स्थिर डेटा सदस्य। इसे self.dict1 कहकर मैं इसे "उदाहरण-बाध्य" बना रहा हूं।

+0

आपको ऑब्जेक्ट से प्राप्त, नई शैली के वर्गों का उपयोग करना चाहिए। – nikow

उत्तर

49

जो आप एक बग के रूप में संदर्भित रखते हैं वह documented है, जो पायथन कक्षाओं का मानक व्यवहार है।

__init__ के बाहर एक डिक घोषित करना जैसा कि आपने शुरू में किया था, क्लास-स्तरीय चर घोषित कर रहा है। यह केवल पहली बार बनाया जाता है, जब भी आप नई वस्तुएं बनाते हैं, यह वही नियम पुन: उपयोग करेगा। इंस्टेंस वैरिएबल बनाने के लिए, आप उन्हें __init__ में घोषित करें; यह उसके जैसा आसान है।

+12

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

+3

यह बेकार नहीं है; यदि आप एक चर को उदाहरणों के माध्यम से जारी रखना चाहते हैं तो आप इसका उपयोग करेंगे। आपकी समस्या यह है कि जब आप x = ए() करते हैं तो केवल __init__ कोड निष्पादित हो जाता है। वर्ग परिवर्तक बनी हुई है। –

+0

ठीक है जब तक घोषणा __init__ में नहीं है, यह सी ++ में "स्थैतिक" डेटा सदस्य जैसा ही है, और "__init__" में घोषणा इसे स्थैतिक होने के कारण स्थिर होने से बदलती है? अब यह स्पष्ट है, धन्यवाद। –

1

जब आप उदाहरण की विशेषता तक पहुंचते हैं, तो कहें, self.foo, python को पहले 'foo' self.__dict__ में मिलेगा। यदि नहीं मिला, तो पाइथन TheClass.__dict__

आपके मामले में dict1 कक्षा ए का है, उदाहरण के लिए नहीं।

+1

यह वास्तव में मेरे कामकाजी मॉडल की मदद करता है कि यह कैसे काम करता है। तो एक बार जब आप अपनी ऑब्जेक्ट में self.x को बाध्य करते हैं (या तो __init__ या कहीं और) भविष्य में self.x लुकअप आपके आवृत्ति के दायरे में उस नए चर को संदर्भित करेगा। यदि वे इसे उदाहरण के दायरे में नहीं पाते हैं तो वे कक्षा के दायरे में दिखते हैं। –

0

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

पायथन ब्लॉग के इतिहास पर an interesting article about the class syntax है। , जहाँ तक मेरा बता सकते हैं

class ClassA: 
    __defaults__['dict1'] = {} 

a = instance(ClassA) 
# a bit of pseudo-code here: 
for name, value in ClassA.__defaults__: 
    a.<name> = value 

कि है सिवाय इसके कि, क्या होता है:

class ClassA: 
    dict1 = {} 
a = ClassA() 

तो फिर तुम शायद इस उम्मीद अजगर के अंदर होने की:

0

इस यदि आपका कोड है एक dict में इसके पॉइंटर की प्रतिलिपि बनाई गई है, मूल्य के बजाय, जो कि पाइथन में हर जगह डिफ़ॉल्ट व्यवहार है। इस कोड को देखो:

a = {} 
b = a 
a['foo'] = 'bar' 
print b 
2

@Matthew: एक वर्ग के सदस्य और ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग में एक वस्तु सदस्य के बीच अंतर की समीक्षा करें। यह समस्या मूल ताना की घोषणा के कारण होती है क्योंकि यह एक वर्ग सदस्य बनाता है, न कि ऑब्जेक्ट सदस्य (जैसा कि मूल पोस्टर का इरादा था।) परिणामस्वरूप, यह कक्षा के सभी उदाहरणों (यानी एक बार) के लिए एक बार मौजूद है (यानी एक बार कक्षा के लिए, कक्षा वस्तु के सदस्य के रूप में स्वयं) तो व्यवहार पूरी तरह से सही है।

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