Accéder à la classe externe de la classe interne en python

j'ai une situation comme ça...

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

Comment puis-je accéder à la méthode de la classe Outer de la classe Inner ?

Modifier -- Merci pour les réponses. Je conclus que j'ai besoin de réévaluer comment j'avais conçu ce qui doit être mis en œuvre et de trouver une méthode plus robuste.

63
demandé sur Artem Likhvar 2010-01-08 02:49:05

9 réponses

les méthodes d'une classe imbriquée ne peuvent pas accéder directement aux attributs d'instance de la classe externe.

notez que ce n'est pas nécessairement le cas qu'une instance de la classe externe existe même lorsque vous avez créé une instance de la classe interne.

en fait, il est souvent recommandé de ne pas utiliser de classes imbriquées, puisque la nidification n'implique pas de relation particulière entre les classes intérieure et extérieure.

42
répondu Daniel Vassallo 2010-01-08 00:02:24

vous essayez d'accéder à L'instance de classe D'Outer, à partir de l'instance de classe interne. Il suffit donc d'utiliser la méthode factory pour construire l'instance interne et lui passer L'instance externe.

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()
36
répondu Kitlbast 2011-08-11 23:44:28

peut - être que je suis fou mais cela semble très facile en effet-la chose est de faire votre classe intérieure à l'intérieur d'une méthode de la classe extérieure...

def do_sthg( self ):
    ...

def messAround( self ):

    outerClassSelf = self

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

Plus... le "soi" n'est utilisé que par convention, de sorte que vous pourriez faire ceci:

def do_sthg( self ):
    ...

def messAround( outerClassSelf ):

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

on pourrait objecter que vous ne pouvez pas créer cette classe intérieure à partir de l'extérieur de la classe extérieure... mais ce n'est pas vrai:

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

puis, quelque part à des kilomètres:

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

même pousser le bouchon un peu et s'étend intérieur de la classe (NB pour obtenir de super() au travail, vous devez modifier la classe de signature de mooble à la "classe mooble( objet )"

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()

plus tard

mrh1997 souleva un point intéressant sur l'héritage Non-commun des classes intérieures livrées en utilisant cette technique. Mais il semble que la solution est assez simple:

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 ))
20
répondu mike rodent 2018-01-19 22:08:13

voulez-vous dire utiliser l'héritage, plutôt que des classes de nidification comme celle-ci? Ce que tu fais n'a pas de sens en Python.

vous pouvez accéder à la méthode Outer en faisant simplement référence à Outer.some_method dans les méthodes de la classe interne, mais cela ne va pas fonctionner comme vous l'attendez. Par exemple, si vous essayez ceci:

class Outer(object):

    def some_method(self):
        # do something

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

...vous obtiendrez une erreur de type lors de l'initialisation d'un objet Inner , car Outer.some_method attend de recevoir une instance Outer comme premier argument. (Dans l'exemple ci-dessus, vous essayez essentiellement d'appeler some_method comme une méthode de classe de Outer .)

4
répondu zenbot 2010-01-08 00:01:25

j'ai créé un code Python pour utiliser un classe externe à partir de son classe interne , basé sur une bonne idée d'un autre réponse pour cette question. Je pense que c'est court, simple et facile à comprendre.

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}

Le code principal, "prêt pour la production" (sans commentaires, etc.). N'oubliez pas de remplacer toutes les valeurs entre crochets (par exemple <x> ) par la valeur désirée.

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>}

explication du fonctionnement de cette méthode (les étapes de base):

  1. créer une fonction appelée _subclass_container pour agir comme un wrapper pour accéder à la variable self , une référence à la classe de niveau supérieur (à partir du code courant à l'intérieur de la fonction).

    1. Créer une variable nommée _parent_class qui est une référence à la variable self de cette fonction, que les sous-classes de _subclass_container puissent y accéder (évite les conflits de nom avec d'autres variables self dans les sous-classes).

    2. retourner la sous-classe/les sous-classes comme un dictionnaire/liste de sorte que le code appelant la fonction _subclass_container peut accéder aux sous-classes à l'intérieur.

  2. dans la fonction __init__ à l'intérieur de la classe de niveau supérieur (ou partout où cela est nécessaire), renvoie les sous-classes de la fonction _subclass_container dans la variable subclasses .

  3. attribuer les sous-classes stockées dans la variable subclasses aux attributs de la classe de niveau supérieur.

quelques conseils pour faciliter les scénarios:

rendre le code pour assigner les sous-classes à la classe de niveau supérieur plus facile à copier et à utiliser dans les classes dérivées de la classe de niveau supérieur qui ont leur __init__ fonction changée:

insérer avant la ligne 12 dans le code principal:

    def _subclass_init(self):

insérer ensuite dans cette fonction les lignes 5-6 (du code principal) et remplacer les lignes 4-7 par le code suivant::

        self._subclass_init(self)

rendre possible l'attribution d'une sous-classe au niveau supérieur lorsqu'il y a nombre / quantité inconnue de sous-classes.

remplacer la ligne 6 par le code suivant::

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

exemple de scénario où cette solution serait utile et où le nom de classe de niveau supérieur devrait être impossible à obtenir:

une classe, appelée" a "( class a: ) est créée. Il y a des sous-classes qui doivent y accéder (le parent). Une sous-classe est appelée "x1". Dans la présente sous-classe, le code a.run_func() est exécuter.

puis une autre classe, appelée "b" est créée, dérivé de la classe "a" ( class b(a): ). Après cela, un certain code exécute b.x1() (appelant la sous-fonction" x1 " de b, une sous-classe dérivée). Cette fonction exécute a.run_func() , appelant la fonction "run_func" de la classe "a", et non la fonction "run_func" de son parent, " b " (comme il se doit), parce que la fonction qui a été définie dans la classe "a" est définie pour se référer à fonction de la classe "a", comme c'était son parent.

cela poserait des problèmes (par exemple si la fonction a.run_func a été supprimée) et la seule solution sans réécriture du code dans la classe a.x1 serait de redéfinir la sous-classe x1 avec un code mis à jour pour toutes les classes dérivées de la classe" a " qui serait évidemment difficile et inutile.

3
répondu Edward 2018-07-23 14:25:21

une autre possibilité:

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 ()
2
répondu tsnorri 2011-07-29 16:53:47

vous pouvez facilement accéder à la classe outer en utilisant metaclass: après la création de la classe outer check it's attribut dict for any classes (or apply any logic you need-mine is just trivial example) and set corresponding values:

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!

en utilisant cette approche, vous pouvez facilement lier et renvoyer deux classes entre eux.

2
répondu grundic 2017-06-06 14:29:34

S'appuyant sur la pensée convaincante de @tsnorri, que la méthode extérieure peut être une méthode statique :

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

maintenant la ligne en question devrait fonctionner au moment où il est effectivement appelé.

la dernière ligne dans le code ci-dessus donne à la classe intérieure une méthode statique qui est un clone de la méthode statique externe.


ceci tire avantage de deux fonctionnalités Python, qui les fonctions sont des objets , et champ d'application est textuelle .

habituellement, la portée locale renvoie aux noms locaux de la fonction courante (textuellement).

...ou actuelle classe dans notre cas. Ainsi, les objets " local "de la définition de la classe extérieure ( Inner et some_static_method ) peuvent être mentionnés directement dans cette définition.

1
répondu Bob Stein 2015-10-20 17:13:28

j'ai trouvé ce .

Modifié à la suite de votre question, c'est de la réponse:

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)

je suis sûr que vous pouvez écrire un décorateur pour ceci ou quelque chose:)

/ modifier: kinda

1
répondu flying sheep 2018-03-08 10:18:47