2010-04-24 20 views
6

Intentando convertir super(B, self).method() en una simple y agradable llamada bubble(). ¿Lo hizo, see below!super() en Python 2.x sin argumentos

¿Es posible obtener referencias a la clase B en este ejemplo?

class A(object): pass 

class B(A): 
    def test(self): 
     test2() 

class C(B): pass 

import inspect 
def test2(): 
    frame = inspect.currentframe().f_back 
    cls = frame.[?something here?] 
    # cls here should == B (class) 

c = C() 
c.test() 

Básicamente, C es hijo de B, B es hijo de A. Luego creamos c del tipo C. Entonces la llamada al c.test() realmente llama al B.test() (a través de la herencia), que llama al test2().

test2() puede obtener el marco principal frame; referencia de código al método a través del frame.f_code; self a través de frame.f_locals['self']; pero type(frame.f_locals['self']) es C (por supuesto), pero no B, donde se define el método.

Cualquier forma de obtener B?

+0

Si puedo preguntar ... ¿para qué necesita esto? Porque casi no creo que algo así pueda ser el mejor método para nada. – Wolph

+0

Estoy tratando de simplificar haciendo 'super (B, self) .test()' para usuarios con algún tipo de llamada 'bubble()' simple. En realidad, ya encontré la manera. –

+0

¿No podría hacer 'super (self .__ class__, self) .test()'? – Wolph

Respuesta

3

Se encontró una manera más corta de hacer super(B, self).test() ->bubble() desde abajo.

(Funciona con herencia múltiple, no requiere argumentos, correcly comporta con sub-clases)

La solución fue usar inspect.getmro(type(back_self)) (donde back_self es una self de destinatario de la llamada), a continuación, iterar como cls con method_name in cls.__dict__ y verificar que la referencia de código que tenemos es la de esta clase (realizada en la función anidada find_class_by_code_object(self)).

bubble() se puede extender fácilmente con *args, **kwargs.

import inspect 
def bubble(*args, **kwargs): 
    def find_class_by_code_object(back_self, method_name, code): 
     for cls in inspect.getmro(type(back_self)): 
      if method_name in cls.__dict__: 
       method_fun = getattr(cls, method_name) 
       if method_fun.im_func.func_code is code: 
        return cls 

    frame = inspect.currentframe().f_back 
    back_self = frame.f_locals['self'] 
    method_name = frame.f_code.co_name 

    for _ in xrange(5): 
     code = frame.f_code 
     cls = find_class_by_code_object(back_self, method_name, code) 
     if cls: 
      super_ = super(cls, back_self) 
      return getattr(super_, method_name)(*args, **kwargs) 
     try: 
      frame = frame.f_back 
     except: 
      return 



class A(object): 
    def test(self): 
     print "A.test()" 

class B(A): 
    def test(self): 
     # instead of "super(B, self).test()" we can do 
     bubble() 

class C(B): 
    pass 

c = C() 
c.test() # works! 

b = B() 
b.test() # works! 

Si alguien tiene una idea mejor, escuchemosla.

Error conocido: (gracias doublep) If C.test = B.test -> "infinite" recursion. Aunque parece poco realista que la clase infantil tenga realmente un método, ha sido = 'ed de uno de los padres.

Bug2 Conocido: (doublep gracias) decoradas métodos no funcionará (probablemente irreparable, ya que devuelve un decorador de cierre) ... proble decorador fijo con for _ in xrange(5): frame = frame.f_back ... - se encargará de hasta 5 decoradores, aumente si es necesario. ¡Me encanta Python!

El rendimiento es 5 veces peor que super() llamada, pero estamos hablando de 200K llamadas frente a un millón de llamadas por segundo, si esto no está en sus bucles más ajustados, no hay razón para preocuparse.

+0

No estoy seguro de que funcione para las funciones decoradas. Además, espero que funcione ce no es importante en su uso para 'bubble()'. – doublep

+0

Otro error (bastante improbable en el uso real): si la subclase 'def foo ...', entonces 'bar = foo' luego' bubble() 'en' bar() 'no funcionará como excepción porque' method_name' será evaluado incorrectamente a "foo". – doublep

+1

bar = foo: si hago 'B.test_alias = B.test' y luego' c.test_alias() ', llama a' A.test() 'como se esperaba. El rendimiento parece estar bien, 5000 ns (nanosegundos) para bubble() vs 900ns para super (...). Esto es una gota, pero no lo veo como uno grande. –

-1

Dado que las funciones en Python no están ligadas a las clases [1], la respuesta general es "imposible". Sin embargo, para funciones no estáticos que siguen la convención de nomenclatura estándar para los atributos que probablemente se podría tratar

inspect.currentframe() .f_back.f_locals [ 'auto']. clase

[1], por ejemplo, se puede hacer:

class X: pass 
X.foo = (lambda self: None) 

EDIT:

I logrado sugerir una forma ya presente en la pregunta: - /. Sin embargo, todavía tengo curiosidad de cómo lograste hacer eso, ya que todavía estoy seguro de que ciertas funciones no están ligadas a las clases en Python.

+0

Realmente posible, lo publicaré en un segundo. (No es necesario para métodos estáticos, ya que estaba buscando un reemplazo para super(), que se ejecuta solo en métodos enlazados) –

+0

@doublep: 1. Intente evaluar 'X.foo.im_class' y se sorprenderá. 2. La solución que propones aparece en la pregunta. – interjay

+1

@interjay: Oh maldita sea, de hecho. Eso es lo que obtengo por no leer hasta el final :(. Sin embargo, 'X.foo.im_class' no ayudará en este caso. Intenta' Y.foo = X.foo'; la función será la misma, pero ' im_class' no: esa es una propiedad del objeto de enlace de método, no de la función. – doublep

0

Aunque este código nunca se debe usar para ningún propósito normal. Por el bien de responder la pregunta, aquí hay algo que funciona;)

import inspect 

def test2(): 
    funcname = inspect.stack()[1][3] 
    frame = inspect.currentframe().f_back 
    self = frame.f_locals['self'] 

    return contains(self.__class__, funcname) 

def contains(class_, funcname): 
    if funcname in class_.__dict__: 
     return class_ 

    for class_ in class_.__bases__: 
     class_ = contains(class_, funcname) 
     if class_: 
      return class_ 
+0

Bueno, eso es básicamente lo que hice a continuación, sin embargo, ¿no crees que el uso de inspect.getmro() es mejor que __bases__? Además, no prueba que la función es realmente la que lo llamó, la clase 'C' también podría tener la función' test'. Mi respuesta se encarga de eso. –

+0

De hecho, simplemente no pude encontrar el método mro así que lo emulé yo mismo. Tu método es definitivamente mejor. Aunque todavía sostengo que ambas son la solución incorrecta. – Wolph

Cuestiones relacionadas