2009-03-12 23 views
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?
P. ej. en este ejemplo:python: determine si una clase está anidada

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 a show_type_info(OuterClass.InnerClass) para mostrar también que InnerClass se define dentro OuterClass.

+0

¿Qué le pasa a sí mismo .__ class __.__ name__? ¿Qué propósito tienes para esta información? –

+0

@ S.Lott: ¿qué quieres decir con "¿qué pasa con uno mismo .__ clase __.__ nombre__"? Lo siento, no entiendo tu pregunta ... –

+0

@orsogufo: Lo que está "mal" - no funciona - no es apropiado - no ayuda - está roto - no hace qué usted quiere - no está en el formato correcto - "mal" con uno mismo .__ clase __.__ nombre__? Algo no está bien con eso, pero no sé lo que no te gusta. Por favor aclara –

Respuesta

12

AFAIK, dada una clase y ninguna otra información, no se puede decir si es una clase anidada o no. Sin embargo, see here para saber cómo puede 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 probablemente no funcione: inspect.getmro, por ejemplo, solo le den clases base, no clases externas.

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

+0

+1 Este es un buen consejo. –

+0

Gracias, aceptó su respuesta: el punto es el hecho de que una clase anidada es un atributo de lo externo. P.S: estoy seguro de que siempre hay alternativas a las clases anidadas, pero no puedo ver por qué deberían ser una mala idea tampoco. –

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 se declara dentro de otra clase, porque eso es sólo una variable de unión 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 interior en el enlace publicado por John es un enfoque interesante, pero yo no lo usaría tal como está. No almacena en caché las clases que crea para cada objeto exterior, por lo que obtener una nueva InnerClass cada vez que llame 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 de Java de hacer las variables de clase exteriores disponible en la clase interna con getattr/setattr es muy dudoso, y realmente innecesario (ya que la manera más pitonica sería llamar i .__ outer __. attr explícitamente).

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.

4

Realmente una clase anidada no es diferente de cualquier otra clase; simplemente se define 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.

por ejemplo:

import inspect 

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

Esto funcionará para los casos más comunes, pero no puede hacer lo que quiera en situaciones donde las clases se cambia el nombre o manipulados de otra manera 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 
1

Gracias a todos por sus respuestas.
He encontrado 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() 
Cuestiones relacionadas