नेस्टेड क्लासेस का स्कोप?


116

मैं पायथन में नेस्टेड कक्षाओं में गुंजाइश समझने की कोशिश कर रहा हूं। यहाँ मेरा उदाहरण कोड है:

class OuterClass:
    outer_var = 1
    class InnerClass:
        inner_var = outer_var

कक्षा का निर्माण पूरा नहीं होता है और मुझे त्रुटि मिलती है:

<type 'exceptions.NameError'>: name 'outer_var' is not defined

कोशिश करने से inner_var = Outerclass.outer_varकाम नहीं चलता। मुझे मिला:

<type 'exceptions.NameError'>: name 'OuterClass' is not defined

मैं स्थैतिक outer_varतक पहुँचने की कोशिश कर रहा हूँ InnerClass

क्या इसे करने का कोई तरीका है?

जवाबों:


105
class Outer(object):
    outer_var = 1

    class Inner(object):
        @property
        def inner_var(self):
            return Outer.outer_var

यह काफी हद तक समान नहीं है क्योंकि अन्य भाषाओं में समान चीजें काम करती हैं, और एक्सेस करने के लिए वैश्विक लुकअप का उपयोग करती हैं outer_var। (यदि आप नाम क्या वस्तु बदल जाते हैंOuter से जुड़ा है, तो यह कोड अगली बार निष्पादित होने पर उस ऑब्जेक्ट का उपयोग करेगा।)

यदि आप इसके बजाय चाहते हैं कि सभी Innerवस्तुओं का संदर्भ होने के Outerकारण outer_varवास्तव में एक उदाहरण विशेषता है:

class Outer(object):
    def __init__(self):
        self.outer_var = 1

    def get_inner(self):
        return self.Inner(self)
        # "self.Inner" is because Inner is a class attribute of this class
        # "Outer.Inner" would also work, or move Inner to global scope
        # and then just use "Inner"

    class Inner(object):
        def __init__(self, outer):
            self.outer = outer

        @property
        def inner_var(self):
            return self.outer.outer_var

ध्यान दें कि पाइथन में घोंसले के शिकार वर्ग कुछ हद तक असामान्य है, और कक्षाओं के बीच किसी भी प्रकार के विशेष संबंध को स्वचालित रूप से लागू नहीं करता है। तुम बेहतर हो घोंसले के शिकार नहीं। (आप अभी भी एक वर्ग विशेषता सेट कर सकते हैं Outerकरने के लिए Inner, यदि आप चाहते हैं।)


2
यह आपके उत्तर के साथ अजगर के किस संस्करण को जोड़ने में सहायक हो सकता है।
ए जे।

1
मैंने इसे मन में 2.6 / 2.x के साथ लिखा था, लेकिन, इसे देखते हुए, मुझे ऐसा कुछ भी नहीं दिख रहा है जो 3.x में समान काम नहीं करेगा।

मैं इस भाग में आपके मतलब के बारे में बहुत कुछ नहीं जानता, "(यदि आप उस वस्तु को बदलते हैं जिसका नाम आउटर है, तो यह कोड अगली बार निष्पादित होने पर उस ऑब्जेक्ट का उपयोग करेगा।)" क्या आप कृपया मुझे समझने में मदद कर सकते हैं?
28:14

1
@batbrat का अर्थ है कि आपके द्वारा किए जाने वाले संदर्भ को Outerहर बार नए सिरे से देखा जाता है Inner.inner_var। इसलिए यदि आप Outerएक नई वस्तु के लिए नाम का खंडन करते हैं , Inner.inner_varतो उस नई वस्तु को वापस करना शुरू कर देंगे।
फेलिप

45

मुझे लगता है कि आप बस कर सकते हैं:

class OuterClass:
    outer_var = 1

    class InnerClass:
        pass
    InnerClass.inner_var = outer_var

इसके कारण आपको जो समस्या हुई है:

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

   class A:  

       a = 42  

       b = list(a + i for i in range(10))

http://docs.python.org/reference/executionmodel.html#naming-and-binding

उपरोक्त का अर्थ है:
एक फ़ंक्शन बॉडी एक कोड ब्लॉक है और एक विधि एक फ़ंक्शन है, फिर एक वर्ग परिभाषा में मौजूद फ़ंक्शन बॉडी से बाहर परिभाषित फ़ंक्शन फ़ंक्शन बॉडी तक विस्तारित नहीं होते हैं।

आपके मामले के लिए इसे
परिभाषित करना : एक वर्ग परिभाषा एक कोड ब्लॉक है, फिर बाहरी वर्ग परिभाषा में मौजूद आंतरिक वर्ग परिभाषा से बाहर के नाम आंतरिक वर्ग परिभाषा तक विस्तारित नहीं होते हैं।


2
गजब का। आपका उदाहरण विफल हो जाता है, "वैश्विक नाम 'ए' परिभाषित नहीं है" का दावा करता है। फिर भी एक सूची समझ का प्रतिस्थापन [a + i for i in range(10)]सफलतापूर्वक एबी को अपेक्षित सूची में बांध देता है [42..51]।
जॉर्ज

6
@George ध्यान दें कि कक्षा A के साथ उदाहरण मेरा नहीं है, यह पायथन आधिकारिक डॉक्टर का है जिसका लिंक मैंने दिया था। यह उदाहरण विफल रहता है और वह विफलता वह है जो इस उदाहरण में दिखाया जाना चाहता है। वास्तव में list(a + i for i in range(10))है list((a + i for i in range(10)))कि कहने के लिए है list(a_generator)। वे कहते हैं कि एक जनरेटर को कार्यों के दायरे से समान दायरे के साथ लागू किया जाता है।
पलक

मेरे लिए @George, इसका मतलब है कि फ़ंक्शन अलग-अलग तरीके से कार्य करते हैं यदि वे एक मॉड्यूल या एक कक्षा में हैं। पहले मामले में, एक फ़ंक्शन एक स्वतंत्र पहचानकर्ता से बंधे ऑब्जेक्ट को खोजने के लिए बाहर जाता है। दूसरे मामले में, एक फ़ंक्शन, जिसे एक विधि कहना है, अपने शरीर के बाहर नहीं जाता है। एक मॉड्यूल में कार्य और एक कक्षा में विधियां वास्तव में दो प्रकार की वस्तुओं में होती हैं। विधियाँ केवल कक्षा में होने वाले कार्य नहीं हैं। यही मेरा विचार है।
पलक

5
@ जॉर्ज: एफडब्ल्यूआईडब्ल्यू, list(...)पाइथन 3 में न तो कॉल और न ही समझ का काम। Py3 के लिए प्रलेखन भी इस बात को दर्शाता है। अब यह कहता है कि " क्लास ब्लॉक में परिभाषित नामों का दायरा वर्ग ब्लॉक तक सीमित है; यह विधियों के कोड ब्लॉक तक विस्तृत नहीं है - इसमें फ़ंक्शन गुंजाइश का उपयोग करके कार्यान्वित किए जाने के बाद से समझ और जनरेटर के भाव शामिल हैं। " (मेरा जोर )।
16

मैं इस बारे में उत्सुक हूं कि सूची (Aa + i for i (10)) भी काम नहीं करती है, जहां मैंने AA द्वारा तय किया है मुझे लगता है कि A एक वैश्विक नाम हो सकता है।
गॉक्सिंग

20

यदि आप सिर्फ नेस्टेड कक्षाओं का उपयोग नहीं करते हैं तो आप बेहतर हो सकते हैं। यदि आपको घोंसला बनाना है, तो यह कोशिश करें:

x = 1
class OuterClass:
    outer_var = x
    class InnerClass:
        inner_var = x

या दोनों वर्गों को घोषित करने से पहले उन्हें घोषित करें:

class OuterClass:
    outer_var = 1

class InnerClass:
    inner_var = OuterClass.outer_var

OuterClass.InnerClass = InnerClass

(इसके बाद del InnerClassयदि आपको आवश्यकता हो तो आप कर सकते हैं ।)


3

सबसे आसान समाधान:

class OuterClass:
    outer_var = 1
    class InnerClass:
        def __init__(self):
            self.inner_var = OuterClass.outer_var

इसके लिए आपको स्पष्ट होना चाहिए, लेकिन अधिक प्रयास नहीं करना चाहिए।


NameError: 'OuterClass' नाम परिभाषित नहीं है - -1
Mr_and_Mrs_D

3
बिंदु इसे बाहरी वर्ग के दायरे से एक्सेस करने के लिए है - यदि आप बाहरी के अंदर एक स्थिर दायरे से इसे कॉल करते हैं तो इसी तरह की त्रुटि होगी। मेरा सुझाव है कि आप इस पोस्ट को हटाएं
Mr_and_Mrs_D

1

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

class OuterClass:
    def __init__(self):
        self.outer_var = 1
        self.inner_class = OuterClass.InnerClass(self)
        print('Inner variable in OuterClass = %d' % self.inner_class.inner_var)

    class InnerClass:
        def __init__(self, outer_class):
            self.outer_class = outer_class
            self.inner_var = 2
            print('Outer variable in InnerClass = %d' % self.outer_class.outer_var)

2
कृपया ध्यान दें कि आपके पास यहां एक संदर्भ चक्र है, और कुछ परिदृश्य में इस वर्ग को मुक्त नहीं किया जाएगा। एक उदाहरण, cPython के साथ, आपके पास परिभाषित __del__ विधि होनी चाहिए , कचरा कलेक्टर संदर्भ चक्र को संभालने में सक्षम नहीं होगा, और वस्तुएं अंदर जाएंगी gc.garbage। उपरोक्त कोड, जैसा है, वैसे समस्याग्रस्त नहीं है। इससे निपटने का तरीका कमजोर संदर्भ का उपयोग कर रहा है । आप कमजोर (2.7) या कमजोर (3.5)
मर्दाना

0

सभी स्पष्टीकरण पायथन डॉक्यूमेंटेशन पायथन ट्यूटोरियल में पाए जा सकते हैं

अपनी पहली त्रुटि के लिए <type 'exceptions.NameError'>: name 'outer_var' is not defined। स्पष्टीकरण है:

तरीकों के भीतर से डेटा विशेषताओं (या अन्य तरीकों!) को संदर्भित करने के लिए कोई आशुलिपि नहीं है। मुझे लगता है कि यह वास्तव में तरीकों की पठनीयता को बढ़ाता है: एक विधि के माध्यम से glancing जब स्थानीय चर और उदाहरण चर को भ्रमित करने का कोई मौका नहीं है।

पायथन ट्यूटोरियल 9.4 से उद्धृत

आपकी दूसरी त्रुटि के लिए <type 'exceptions.NameError'>: name 'OuterClass' is not defined

जब एक वर्ग परिभाषा सामान्य रूप से (अंत के माध्यम से) छोड़ दी जाती है, तो एक वर्ग वस्तु बनाई जाती है।

पायथन ट्यूटोरियल 9.3.1 से उद्धृत

इसलिए जब आप कोशिश करते हैं inner_var = Outerclass.outer_var, तो Quterclassअभी तक नहीं बनाया गया है, इसीलिएname 'OuterClass' is not defined

आपकी पहली त्रुटि के लिए एक अधिक विस्तृत लेकिन थकाऊ विवरण:

हालाँकि, कक्षाओं में एन्कोडिंग फ़ंक्शंस के स्कोप्स तक पहुँच होती है, हालाँकि, वे क्लास के भीतर नेस्टेड कोड को एनकोडिंग स्कोप्स के रूप में कार्य नहीं करते हैं: पाइथन संदर्भित नामों के लिए एन्क्लोज़िंग फ़ंक्शंस की खोज करता है, लेकिन कभी भी कोई एनक्लोज़िंग क्लासेस नहीं। यही है, एक वर्ग एक स्थानीय क्षेत्र है और स्थानीय स्कोपों ​​को घेरने की पहुंच है, लेकिन यह नेस्टेड कोड के लिए एक स्थानीय स्कोप के रूप में काम नहीं करता है।

Learning.Python (5th) .Mark.Lutz से उद्धृत

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