Question

Supposons que vous ayez une méthode python qui obtient un type en tant que paramètre. est-il possible de déterminer si le type donné est une classe imbriquée?
Par exemple. dans cet exemple:

def show_type_info(t):
    print t.__name__
    # print outer class name (if any) ...

class SomeClass:
    pass

class OuterClass:
    class InnerClass:
        pass

show_type_info(SomeClass)
show_type_info(OuterClass.InnerClass)

J'aimerais que l'appel à show_type_info (OuterClass.InnerClass) montre également que InnerClass est défini dans OuterClass.

Était-ce utile?

La solution

D'après ce que je sais, étant donné une classe et aucune autre information, vous ne pouvez pas savoir s'il s'agit ou non d'une classe imbriquée. Cependant, voir ici pour savoir comment utiliser un décorateur pour le déterminer.

Le problème est qu’une classe imbriquée est simplement une classe normale qui est un attribut de sa classe externe. Les autres solutions que vous pouvez espérer travailler ne fonctionneront probablement pas - inspect.getmro , par exemple, ne vous donne que des classes de base, pas des classes externes.

De plus, les classes imbriquées sont rarement nécessaires. Je voudrais fortement reconsidérer si c'est une bonne approche dans chaque cas particulier où vous vous sentez tenté d'en utiliser une.

Autres conseils

Une classe interne n'offre aucune particularité particulière en Python. C'est seulement une propriété de l'objet de classe, pas différente d'une propriété d'entier ou de chaîne. Votre exemple OuterClass / InnerClass peut être réécrit exactement comme:

class OuterClass(): pass
class InnerClass(): pass
OuterClass.InnerClass= InnerClass

InnerClass ne peut pas savoir si elle a été déclarée dans une autre classe, car il ne s'agit que d'une simple liaison de variable. La magie qui permet aux méthodes liées de connaître leur propriétaire elles-mêmes & # 8217; ne s'applique pas ici.

La magie du décorateur de la classe intérieure dans le lien que John a posté est une approche intéressante, mais je ne l’utiliserais pas telle quelle. Il ne met pas en cache les classes qu'il crée pour chaque objet externe. Vous obtenez donc une nouvelle InnerClass chaque fois que vous appelez outerinstance.InnerClass:

>>> o= OuterClass()
>>> i= o.InnerClass()
>>> isinstance(i, o.InnerClass)
False # huh?
>>> o.InnerClass is o.InnerClass
False # oh, whoops...

De plus, la façon dont il tente de reproduire le comportement Java consistant à rendre les variables de classe externe disponibles sur la classe interne avec getattr / setattr est très louche et vraiment inutile (dans la mesure où la méthode la plus Pythonique serait d'appeler explicitement i .__ outer __. attr ).

Vraiment, une classe imbriquée n'est pas différente de toute autre classe - elle se trouve simplement définie ailleurs que dans l'espace de noms de premier niveau (à la place d'une autre classe). Si nous modifions la description à partir de " imbriqué " "Vous ne pourrez peut-être pas vous approcher suffisamment de ce dont vous avez besoin.

par exemple:

import inspect

def not_toplevel(cls):
    m = inspect.getmodule(cls)
    return not (getattr(m, cls.__name__, []) is cls)

Cela fonctionnera pour les cas courants, mais il se peut que cela ne fasse pas ce que vous voulez dans les situations où les classes sont renommées ou manipulées d'une autre manière après la définition. Par exemple:

class C:             # not_toplevel(C) = False
    class B: pass    # not_toplevel(C.B) = True

B=C.B                # not_toplevel(B) = True

D=C                  # D is defined at the top, but...
del C                # not_toplevel(D) = True

def getclass():      # not_toplevel(getclass()) = True
    class C: pass

Merci à tous pour vos réponses.
J'ai trouvé cette solution possible en utilisant des métaclasses; Je l'ai fait plus pour l'obstination que pour le besoin réel, et ce d'une manière qui ne sera pas applicable à Python 3.
Je veux quand même partager cette solution, alors je la poste ici.

#!/usr/bin/env python
class ScopeInfo(type): # stores scope information
    __outers={} # outer classes
    def __init__(cls, name, bases, dict):
        super(ScopeInfo, cls).__init__(name, bases, dict)
        ScopeInfo.__outers[cls] = None
        for v in dict.values(): # iterate objects in the class's dictionary
            for t in ScopeInfo.__outers:
                if (v == t): # is the object an already registered type?
                    ScopeInfo.__outers[t] = cls
                    break;
    def FullyQualifiedName(cls):
        c = ScopeInfo.__outers[cls]
        if c is None:
            return "%s::%s" % (cls.__module__,cls.__name__)
        else:
            return "%s.%s" % (c.FullyQualifiedName(),cls.__name__)

__metaclass__ = ScopeInfo

class Outer:
    class Inner:
        class EvenMoreInner:
            pass

print Outer.FullyQualifiedName()
print Outer.Inner.FullyQualifiedName()
print Outer.Inner.EvenMoreInner.FullyQualifiedName()
X = Outer.Inner
del Outer.Inner
print X.FullyQualifiedName()

Si vous ne le définissez pas vous-même, je ne crois pas qu'il soit possible de déterminer si la classe est imbriquée. Comme de toute façon une classe Python ne peut pas être utilisée comme un espace de noms (ou du moins pas facilement), je dirais que la meilleure chose à faire est simplement d'utiliser des fichiers différents.

Depuis Python 3.3, il existe un nouvel attribut __ qualname __ , qui fournit non seulement le nom de la classe, mais également les noms des classes externes:

Dans votre exemple, cela entraînerait:

assert SomeClass.__qualname__ == 'SomeClass'
assert OuterClass.InnerClass.__qualname__ == 'OuterClass.InnerClass'

Si __ qualname __ d'une classe ne contient pas un '.' c'est une classe extérieure!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top