आंतरिक कक्षा से बाहरी वर्ग तक कैसे पहुंचें?


102

मेरे पास ऐसी स्थिति है ...

class Outer(object):

    def some_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            self.Outer.some_method()    # <-- this is the line in question

मैं Outerकक्षा की विधि को Innerकक्षा से कैसे एक्सेस कर सकता हूं ?


आप यह क्यों कर रहे हैं? सरल सहकर्मी रिश्तों में क्या गलत है? क्या आप कुछ छुपाने की कोशिश कर रहे हैं?
एस.लॉट S

1
इस परिदृश्य का एक उदाहरण उप-वर्गों के साथ एक वर्ग हो सकता है जिसे बाहरी वर्ग तक पहुंचने की आवश्यकता है, सामान्य रूप से, लेकिन फिर पहली कक्षा से प्राप्त एक और वर्ग (शीर्ष स्तर) बनाने की आवश्यकता है। उस स्थिति में, द्वितीय श्रेणी की उप-कक्षाएं मूल का उपयोग करके अभिभावक तक पहुंचने की कोशिश करेंगी self.<original_parent_name>और मूल वर्ग प्राप्त करेंगी , न कि नई कक्षा जिससे वे उप-वर्ग हैंमुझे उम्मीद है कि इसे पढ़ने वाले लोग इस कठिन परिदृश्य की कल्पना कर सकते हैं और इस तरह के प्रश्नों के बिंदु को देख सकते हैं।
एडवर्ड

1
का डुप्लीकेट stackoverflow.com/questions/2278426 और यह एक अच्छा जवाब है।
xmedeko

जवाबों:


68

एक नेस्टेड क्लास के तरीके सीधे बाहरी वर्ग की इंस्टेंस विशेषताओं तक नहीं पहुँच सकते हैं।

ध्यान दें कि यह जरूरी नहीं है कि बाहरी वर्ग का उदाहरण तब भी मौजूद हो जब आपने आंतरिक वर्ग का एक उदाहरण बनाया हो।

वास्तव में, यह अक्सर नेस्टेड वर्गों का उपयोग करने के खिलाफ सिफारिश की जाती है, क्योंकि नेस्टिंग आंतरिक और बाहरी वर्गों के बीच किसी विशेष संबंध का मतलब नहीं है।


1
हम्म, पायथन जावा / सी ++ की तुलना में डरावना है ... नीचे मेरा जवाब देखें। यदि हम बालों को विभाजित कर रहे हैं, जो हम आम तौर पर कर रहे हैं, तो मैं वास्तव में आपको यह नहीं बता सकता कि क्या मेरे "नेस्टेड क्लास इन मेथड" को एक आंतरिक वर्ग के रूप में गिना जाता है। इस बिंदु पर, हालांकि, मुझे बतख टाइपिंग को आमंत्रित करना है: अगर यह सब कुछ करता है तो एक आंतरिक वर्ग संभवतः कर सकता है ... पाइथोनिक दृष्टिकोण से यह संभवतः विभाजित बालों से ऊबने का समय है
माइक कृंतक

21
पाठ्यक्रम का एक आंतरिक वर्ग बाहरी वर्ग के साथ एक संबंध स्थापित करता है, जो आमतौर पर आंतरिक वर्ग या अन्यथा एक संगठनात्मक नाम स्थान के निहित उपयोग के दायरे के साथ होता है।
एक्यूमेनस

61

आप आंतरिक श्रेणी के उदाहरण से आउटर के वर्ग उदाहरण का उपयोग करने का प्रयास कर रहे हैं। तो बस कारखाने-विधि का उपयोग करने के लिए आंतरिक उदाहरण बनाने के लिए और बाहरी उदाहरण इसे पारित करने के लिए।

class Outer(object):

    def createInner(self):
        return Outer.Inner(self)

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

        def inner_method(self):
            self.outer_instance.anothermethod()

यह बिल्कुल सही उत्तर है - इसे सही उत्तर के रूप में चुना जाना चाहिए क्योंकि यह ओपी के प्रश्न का समाधान प्रदान करता है - बहुत बहुत धन्यवाद!
डैनियल

28

शायद मैं पागल हूँ, लेकिन यह वास्तव में बहुत आसान लगता है - बात यह है कि बाहरी वर्ग की एक विधि के अंदर अपने आंतरिक वर्ग को बनाने के लिए ...

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

    class mooble():
        def do_sthg_different( self ):
            ...
            outerClassSelf.do_sthg()

प्लस ... "स्व" केवल सम्मेलन द्वारा उपयोग किया जाता है, इसलिए आप ऐसा कर सकते हैं:

def do_sthg( self ):
    ...

def messAround( outerClassSelf ):

    class mooble():
        def do_sthg_different( self ):
            ...
            outerClassSelf.do_sthg()

यह आपत्ति हो सकती है कि आप बाहरी वर्ग के बाहर से इस आंतरिक वर्ग का निर्माण नहीं कर सकते ... लेकिन यह सच नहीं है:

class Bumblebee():

    def do_sthg( self ):
        print "sthg"

    def giveMeAnInnerClass( outerClassSelf ):

        class mooble():
            def do_sthg_different( self ):
                print "something diff\n"
                outerClassSelf.do_sthg()
        return mooble

फिर, कहीं मीलों दूर:

blob = Bumblebee().giveMeAnInnerClass()()
blob.do_sthg_different()    

यहां तक ​​कि नाव को थोड़ा बाहर धकेलें और इस आंतरिक वर्ग (एनबी को सुपर () प्राप्त करने के लिए विस्तारित करें, ताकि आपको mooble के वर्ग हस्ताक्षर को "क्लास मुबल (ऑब्जेक्ट)" में बदलना पड़े

class InnerBumblebeeWithAddedBounce( Bumblebee().giveMeAnInnerClass() ):
    def bounce( self ):
        print "bounce"

    def do_sthg_different( self ):
        super( InnerBumblebeeWithAddedBounce, self ).do_sthg_different()
        print "and more different"


ibwab = InnerBumblebeeWithAddedBounce()    
ibwab.bounce()
ibwab.do_sthg_different()

बाद में

mrh1997 ने इस तकनीक का उपयोग करके वितरित आंतरिक वर्गों की गैर-सामान्य विरासत के बारे में एक दिलचस्प बिंदु उठाया। लेकिन ऐसा लगता है कि समाधान बहुत सीधा है:

class Fatty():
    def do_sthg( self ):
        pass

    class InnerFatty( object ):
        pass

    def giveMeAnInnerFattyClass(self):
        class ExtendedInnerFatty( Fatty.InnerFatty ):
            pass
        return ExtendedInnerFatty

fatty1 = Fatty()
fatty2 = Fatty()

innerFattyClass1 = fatty1.giveMeAnInnerFattyClass()
innerFattyClass2 = fatty2.giveMeAnInnerFattyClass()

print ( issubclass( innerFattyClass1, Fatty.InnerFatty ))
print ( issubclass( innerFattyClass2, Fatty.InnerFatty ))

1
यह मेरे लिए काम करता है। इस निर्माण को वास्तव में क्या कहा जाता है? एक कारखाना समारोह? एक बंद?
नग्नफैनेटिक

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

अच्छा कार्य। इस समाधान विचार और थोड़ी सोच से प्रेरित होकर, मैंने कुछ और स्पष्टीकरण के साथ नीचे एक और उत्तर बनाने के लिए इस उत्तर के कुछ हिस्सों का विस्तार किया।
एडवर्ड

1
@mikerodent आपकी टिप्पणी के कारण (मेरे उत्तर पर), मैंने अब "सामुदायिक विकि" विवरण को हटाने के लिए अपने उत्तर को संपादित कर दिया है। आपकी तरह, मुझे "समुदाय विकि" उत्तरों के बारे में कोई ज्ञान नहीं है, इसलिए यह अच्छा है कि आपने अपनी गलती को ठीक कर लिया है।
एडवर्ड

@mikerodent इसके अलावा, कोई अपराध नहीं है, लेकिन मुझे नहीं लगता कि "समुदाय विकि" उत्तरों में "समुदाय विकि" टैग है, उन्हें बस एक प्रकार का उत्तर होना चाहिए। यदि आप रुचि रखते हैं, तो स्टैक ओवरफ्लो पर "सामुदायिक विकि" उत्तरों के विवरण के साथ एक सहायता पृष्ठ होगा।
एडवर्ड

4

क्या आप इस तरह के घोंसले के शिकार वर्गों के बजाय विरासत का उपयोग करने का मतलब है? आप जो कर रहे हैं, वह पायथन में समझ का ढेर नहीं है।

आप Outerकेवल Outer.some_methodआंतरिक वर्ग के तरीकों के भीतर संदर्भित करके 's some_method तक पहुँच सकते हैं , लेकिन यह आपकी इच्छानुसार काम नहीं करने वाला है। उदाहरण के लिए, यदि आप यह कोशिश करते हैं:

class Outer(object):

    def some_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            Outer.some_method()

... किसी Innerऑब्जेक्ट को इनिशियलाइज़ करते समय आपको TypeError मिल जाएगी , क्योंकि इसके पहले तर्क के रूप में Outer.some_methodएक Outerउदाहरण प्राप्त करने की उम्मीद है । (ऊपर दिए गए उदाहरण में, आप मूल some_methodरूप से कक्षा विधि के रूप में कॉल करने का प्रयास कर रहे हैं Outer।)


1
इसका कारण शायद यह समझ में नहीं आता है क्योंकि मैं जानबूझकर हैक किया जा रहा हूं। Django में एक QuerySet के लिए कस्टम तरीकों को जोड़ने के लिए बॉयलरप्लेट कोड की एक बिट की आवश्यकता होती है, और मैं अजगर का उपयोग करने के लिए एक चतुर तरीका प्राप्त करने का प्रयास कर रहा था जिसने मुझे बॉयलरप्लेट कोड को टेम्पलेट करने की अनुमति दी और बस अपने मॉडल कोड में प्रासंगिक भागों को लिखा।
टी। स्टोन

माफी - मैं Django नहीं जानता और इसलिए बॉयलरप्लेट कोड को टेम्पलेट करने का एक तरीका नहीं सुझा सकता, लेकिन आप अपनी कक्षाओं को घोंसले बनाने की कोशिश में गलत पेड़ को भौंक सकते हैं। आपका आंतरिक वर्ग आपके बाहरी वर्ग से कुछ भी हासिल नहीं करता है। बाहरी आउटर के भीतर सभी घोंसले के शिकार आपको केवल सादे इनर के बजाय, आउटर के माध्यम से इसे एक्सेस करने के लिए मजबूर करते हैं।
ज़ेनबोट

3

आप मेटाक्लास का उपयोग करके आसानी से बाहरी वर्ग तक पहुँच सकते हैं: बाहरी वर्ग की जाँच के निर्माण के बाद यह किसी भी वर्ग के लिए विशिष्ट है (या आपकी आवश्यकता के किसी भी तर्क को लागू करें - मेरा सिर्फ तुच्छ उदाहरण है) और संबंधित मान सेट करें:

import six
import inspect


# helper method from `peewee` project to add metaclass
_METACLASS_ = '_metaclass_helper_'
def with_metaclass(meta, base=object):
    return meta(_METACLASS_, (base,), {})


class OuterMeta(type):
    def __new__(mcs, name, parents, dct):
        cls = super(OuterMeta, mcs).__new__(mcs, name, parents, dct)
        for klass in dct.values():
            if inspect.isclass(klass):
                print("Setting outer of '%s' to '%s'" % (klass, cls))
                klass.outer = cls

        return cls


# @six.add_metaclass(OuterMeta) -- this is alternative to `with_metaclass`
class Outer(with_metaclass(OuterMeta)):
    def foo(self):
        return "I'm outer class!"

    class Inner(object):
        outer = None  # <-- by default it's None

        def bar(self):
            return "I'm inner class"


print(Outer.Inner.outer)
>>> <class '__main__.Outer'>
assert isinstance(Outer.Inner.outer(), Outer)

print(Outer().foo())
>>> I'm outer class!
print(Outer.Inner.outer().foo())
>>> I'm outer class!
print(Outer.Inner().outer().foo())
>>> I'm outer class!
print(Outer.Inner().bar())
>>> I'm inner class!

इस दृष्टिकोण का उपयोग करके, आप आसानी से एक दूसरे के बीच दो वर्गों को बाँध सकते हैं और संदर्भित कर सकते हैं।


3

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

class higher_level__unknown_irrelevant_name__class:
    def __init__(self, ...args...):
        ...other code...
        # Important lines to access sub-classes.
        subclasses = self._subclass_container()
        self.some_subclass = subclasses["some_subclass"]
        del subclasses # Free up variable for other use.

    def sub_function(self, ...args...):
        ...other code...

    def _subclass_container(self):
        _parent_class = self # Create access to parent class.
        class some_subclass:
            def __init__(self):
                self._parent_class = _parent_class # Easy access from self.
                # Optional line, clears variable space, but SHOULD NOT BE USED
                # IF THERE ARE MULTIPLE SUBCLASSES as would stop their parent access.
                #  del _parent_class
        class subclass_2:
            def __init__(self):
                self._parent_class = _parent_class
        # Return reference(s) to the subclass(es).
        return {"some_subclass": some_subclass, "subclass_2": subclass_2}

मुख्य कोड, "प्रोडक्शन रेडी" (टिप्पणियों के बिना, आदि)। <x>वांछित मान के साथ कोण कोष्ठक (जैसे ) में प्रत्येक मान को प्रतिस्थापित करना याद रखें ।

class <higher_level_class>:
    def __init__(self):
        subclasses = self._subclass_container()
        self.<sub_class> = subclasses[<sub_class, type string>]
        del subclasses

    def _subclass_container(self):
        _parent_class = self
        class <sub_class>:
            def __init__(self):
                self._parent_class = _parent_class
        return {<sub_class, type string>: <sub_class>}

यह विधि कैसे काम करती है इसका स्पष्टीकरण (मूल चरण):

  1. _subclass_containerचर तक पहुंचने के लिए आवरण के रूप में कार्य करने के लिए नामित फ़ंक्शन बनाएं self, उच्च स्तरीय वर्ग (फ़ंक्शन के अंदर चलने वाले कोड से) के लिए एक संदर्भ।

    1. एक चर बनाएं जिसका नाम इस फ़ंक्शन _parent_classके चर selfके लिए एक संदर्भ है , जो उप-वर्गों _subclass_containerतक पहुंच सकता है ( selfउपवर्गों में अन्य चर के साथ नाम के टकराव से बचा जाता है )।

    2. उप-वर्ग / उप-वर्गों को एक शब्दकोश / सूची के रूप में लौटाएं ताकि _subclass_containerफ़ंक्शन को कॉल करने वाला कोड उप-कक्षाओं तक पहुंच सके।

  2. __init__उच्च स्तर वर्ग (या जहाँ कहीं भी आवश्यकता होती है) के अंदर फ़ंक्शन में, फ़ंक्शन से लौटे हुए उप-वर्गों _subclass_containerको चर में प्राप्त करते हैं subclasses

  3. subclassesउच्च स्तरीय वर्ग की विशेषताओं के लिए चर में संग्रहीत उप-वर्गों को असाइन करें ।

परिदृश्यों को आसान बनाने के लिए कुछ सुझाव:

उप-कक्षाओं को उच्च स्तर की कक्षा में असाइन करने के लिए कोड बनाना आसान है और उच्च स्तर की कक्षा से प्राप्त कक्षाओं में उपयोग किया जा सकता है जो आपके __init__ कार्य की सूची है:

मुख्य कोड में लाइन 12 से पहले डालें:

def _subclass_init(self):

फिर इस फ़ंक्शन लाइनों में 5-6 (मुख्य कोड का) डालें और लाइनों को निम्नलिखित कोड से प्रतिस्थापित करें:

self._subclass_init(self)

उपवर्गों के कई / अज्ञात मात्रा में होने पर उपवर्ग को उच्च स्तर की कक्षा में सौंपना संभव बनाता है।

निम्नलिखित कोड के साथ लाइन 6 बदलें:

for subclass_name in list(subclasses.keys()):
    setattr(self, subclass_name, subclasses[subclass_name])

उदाहरण का परिदृश्य जहां यह समाधान उपयोगी होगा और जहां उच्च स्तर के वर्ग का नाम प्राप्त करना असंभव होना चाहिए:

एक वर्ग, जिसका नाम "ए" ( class a:) बनाया गया है। इसमें उपवर्ग होते हैं जिन्हें इसे (अभिभावक) तक पहुँचाने की आवश्यकता होती है। एक उपवर्ग को "X1" कहा जाता है। इस उपवर्ग में, कोड a.run_func()चलाया जाता है।

फिर एक और वर्ग, नाम "ख" बनाया जाता है, व्युत्पन्न वर्ग से "एक" ( class b(a):)। उसके बाद, कुछ कोड चलता है b.x1()(बी, एक व्युत्पन्न उप-वर्ग के उप फ़ंक्शन "एक्स 1" को कॉल करके)। इस समारोह रन a.run_func(), समारोह वर्ग के "run_func" "एक", बुला नहीं , (एकदम सही ढंग से) समारोह उसकी पेरेंट "run_func", "बी" क्योंकि समारोह कौन सी क्लास में परिभाषित किया गया था "एक" सेट कर दिया जाता उल्लेख करने के लिए कक्षा "ए" के कार्य के रूप में, इसके माता-पिता थे।

इससे समस्याएँ उत्पन्न होंगी (जैसे कि यदि फ़ंक्शन a.run_funcको हटा दिया गया है) और कक्षा में कोड को फिर से लिखने के बिना एकमात्र समाधान "क्लास" से प्राप्त सभी वर्गों के लिए अद्यतन कोड के साथ a.x1उप-वर्ग को फिर से परिभाषित x1करना होगा जो स्पष्ट रूप से मुश्किल होगा और लायक नहीं होगा। यह।


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

3

मुझे यह मिल गया ।

आपके प्रश्न के अनुरूप होने के लिए तैयार:

class Outer(object):
    def some_method(self):
        # do something

    class _Inner(object):
        def __init__(self, outer):
            outer.some_method()
    def Inner(self):
        return _Inner(self)

मुझे यकीन है कि आप किसी तरह इस या कुछ के लिए एक डेकोरेटर लिख सकते हैं

संबंधित: अजगर के आंतरिक वर्गों का उद्देश्य क्या है?


2

एक अन्य संभावना:

class _Outer (object):
    # Define your static methods here, e.g.
    @staticmethod
    def subclassRef ():
        return Outer

class Outer (_Outer):
    class Inner (object):
        def outer (self):
            return _Outer

        def doSomething (self):
            outer = self.outer ()
            # Call your static mehthods.
            cls = outer.subclassRef ()
            return cls ()

1

@Snorri की दलित सोच पर विस्तार करते हुए, कि बाहरी विधि एक स्थिर विधि हो सकती है :

class Outer(object):

    @staticmethod
    def some_static_method(self):
        # do something

    class Inner(object):
        def __init__(self):
            self.some_static_method()    # <-- this will work later

    Inner.some_static_method = some_static_method

अब प्रश्न में रेखा को उस समय तक काम करना चाहिए जब वह वास्तव में कहा जाता है।

उपरोक्त कोड में अंतिम पंक्ति इनर क्लास को एक स्थिर विधि देती है जो बाहरी स्थैतिक विधि का एक क्लोन है।


यह दो पायथन विशेषताओं का लाभ उठाता है, जो कार्य ऑब्जेक्ट हैं , और स्कोप शाब्दिक है

आमतौर पर, स्थानीय स्कोप (पाठ) वर्तमान फ़ंक्शन के स्थानीय नामों का संदर्भ देता है।

... या हमारे मामले में वर्तमान वर्ग । तो बाहरी वर्ग की परिभाषा में "स्थानीय" वस्तुओं ( Innerऔर some_static_method) को सीधे उस परिभाषा के भीतर संदर्भित किया जा सकता है।


1

कुछ साल पार्टी के लिए देर से .... लेकिन पर विस्तार करने के लिए @mike rodentकी अद्भुत जवाब, मैं अपने ही उदाहरण है कि शो के नीचे उपलब्ध कराया है कितना लचीला उसके समाधान है, और यही कारण है कि यह होना चाहिए (या होना चाहिए है किया गया) स्वीकार किए जाते हैं जवाब।

पायथन 3.7

class Parent():

    def __init__(self, name):
        self.name = name
        self.children = []

    class Inner(object):
        pass

    def Child(self, name):
        parent = self
        class Child(Parent.Inner):
            def __init__(self, name):
                self.name = name
                self.parent = parent
                parent.children.append(self)
        return Child(name)



parent = Parent('Bar')

child1 = parent.Child('Foo')
child2 = parent.Child('World')

print(
    # Getting its first childs name
    child1.name, # From itself
    parent.children[0].name, # From its parent
    # Also works with the second child
    child2.name,
    parent.children[1].name,
    # Go nuts if you want
    child2.parent.children[0].name,
    child1.parent.children[1].name
)

print(
    # Getting the parents name
    parent.name, # From itself
    child1.parent.name, # From its children
    child2.parent.name,
    # Go nuts again if you want
    parent.children[0].parent.name,
    parent.children[1].parent.name,
    # Or insane
    child2.parent.children[0].parent.children[1].parent.name,
    child1.parent.children[1].parent.children[0].parent.name
)


# Second parent? No problem
parent2 = Parent('John')
child3 = parent2.Child('Doe')
child4 = parent2.Child('Appleseed')

print(
    child3.name, parent2.children[0].name,
    child4.name, parent2.children[1].name,
    parent2.name # ....
)

आउटपुट:

Foo Foo World World Foo World
Bar Bar Bar Bar Bar Bar Bar
Doe Doe Appleseed Appleseed John

फिर, एक अद्भुत जवाब, आप माइक के लिए सहारा!


-2

यह बहुत आसान है:

इनपुट:

class A:
    def __init__(self):
        pass

    def func1(self):
        print('class A func1')

    class B:
        def __init__(self):
            a1 = A()
            a1.func1()

        def func1(self):
            print('class B func1')

b = A.B()
b.func1()

उत्पादन

कक्षा एक func1

वर्ग बी func1


2
हम्म, सवाल पर ध्यान दें, और शब्द बाहरी वर्ग तक पहुंचते हैं । एक खुश, काफी मनोरंजक, संयोग से, आपके बाहरी और आंतरिक दोनों वर्गों में एक विधि है func1। और, समान रूप से मनोरंजक, आप Aनिर्माणकर्ता के अंदर बनाते हैं B, जो Aउस विशेष रूप से बाहरी वर्ग वस्तु नहीं है B
माइक कृंतक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.