Aquí podría ser tu PUBLICIDAD


python: determina si una clase está anidada

votos
5

Supongamos que tiene un método de python que obtiene un tipo como parámetro; ¿es posible determinar si el tipo dado es una clase anidada?
Por ejemplo, en este ejemplo:

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)

Me gustaría que la llamada show_type_info(OuterClass.InnerClass)también muestre que InnerClass se define dentro de OuterClass.

Publicado el 12/03/2009 a las 16:26
fuente por usuario Paolo Tedesco
En otros idiomas...        العربية       

6 respuestas

votos
12

AFAIK, dada una clase y ninguna otra información, no se puede decir si es una clase anidada. Sin embargo, mira aquí para ver cómo puedes usar un decorador para determinar esto.

El problema es que una clase anidada es simplemente una clase normal que es un atributo de su clase externa. Es probable que otras soluciones que podría esperar que funcionen inspect.getmro, por ejemplo, no solo le darán clases base, no clases externas.

Además, las clases anidadas rara vez se necesitan. Reconsideraría firmemente si ese es un buen enfoque en cada caso particular en el que te sientas tentado a usar uno.

Respondida el 12/03/2009 a las 04:34
fuente por usuario John Feminella


Aquí podría ser tu PUBLICIDAD


votos
5

Una clase interna no ofrece características especiales particulares en Python. Es solo una propiedad del objeto de clase, no diferente de un entero o propiedad de cadena. Su ejemplo OuterClass / InnerClass se puede reescribir exactamente como:

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

InnerClass no puede saber si fue declarado dentro de otra clase, porque eso es solo un enlace variable simple. La magia que hace que los métodos enlazados sepan sobre su propio "dueño" no se aplica aquí.

La magia del decorador de la clase interna en el enlace que John publicó es un enfoque interesante, pero yo no lo usaría tal como está. No almacena en caché las clases que crea para cada objeto externo, por lo que obtienes un nuevo InnerClass cada vez que llamas a outerinstance.InnerClass:

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

También la forma en que trata de replicar el comportamiento Java de hacer que las variables de clase externa estén disponibles en la clase interna con getattr / setattr es muy dudosa e innecesaria (ya que la manera más pitonica sería llamar a i .__ outer __. Attr explícitamente).

Respondida el 12/03/2009 a las 05:23
fuente por usuario bobince

votos
4

Realmente, una clase anidada no es diferente de cualquier otra clase; simplemente está definida en otro lugar que no sea el espacio de nombres de nivel superior (dentro de otra clase en su lugar). Si modificamos la descripción de "anidado" a "no de nivel superior", entonces podrá acercarse lo suficiente a lo que necesita.

p.ej:

import inspect

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

Esto funcionará para casos comunes, pero puede no hacer lo que desee en situaciones donde las clases se renombran o manipulan después de la definición. Por ejemplo:

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
Respondida el 12/03/2009 a las 05:46
fuente por usuario Brian

votos
1

Gracias a todos por sus respuestas o comentarios.
Encontré esta posible solución usando metaclases; Lo he hecho más por obstinación que por necesidad real, y está hecho de una manera que no será aplicable a python 3.
Quiero compartir esta solución de todos modos, así que la estoy publicando aquí.

#!/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()
Respondida el 12/03/2009 a las 06:14
fuente por usuario Paolo Tedesco

votos
0

Comenzando con Python 3.3 hay un nuevo atributo __qualname__ , que proporciona no sólo el nombre de la clase, sino también los nombres de las clases externas:

En su muestra de esto se traduciría en:

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

Si __qualname__ de una clase no contiene un '' se trata de una clase externa!

Respondida el 22/04/2018 a las 03:53
fuente por usuario mrh1997

votos
0

Si no lo configura usted mismo, no creo que haya ninguna forma de determinar si la clase está anidada. Como de todos modos una clase de Python no puede usarse como un espacio de nombres (o al menos no es fácil), yo diría que lo mejor es simplemente usar diferentes archivos.

Respondida el 12/03/2009 a las 05:37
fuente por usuario Nikwin