2010-09-19 13 views
6

Estoy escribiendo un código para determinar el nombre al que está asignado un objeto. Esto es para el trabajo de depuración general y para familiarizarme aún más con las partes internas de Python.Accediendo al nombre que un objeto que se está creando está asignado a

Lo tengo estructurado como un decorador de clases para que todas las instancias de esa clase tengan sus nombres registrados si es posible hacerlo. El código es bastante largo, así que no lo publicaré a menos que me lo pidan. La técnica general es el siguiente, aunque

  1. decorar la clase __init__ método con el código para hacer lo que quiero

  2. conjunto caller = inspect.currentframe().f_back y abierto inspect.getframeinfo(caller).filename y enviarlo a ast.parse. No hago ningún error al verificar aquí porque (1) esto es solo para la depuración/creación de perfiles/piratería (2) este proceso exacto fue 'simplemente' completado o el código no se estaría ejecutando. ¿Hay algún problema con esto?

  3. encontrar la instancia ast.Assignment que hace que el __init__ método actualmente en ejecución para ejecutar

  4. len(assignment.targets) == 1 si entonces no es sólo un elemento en el lado izquierdo, y puedo obtener el nombre de targets[0].id. En una forma simple como a = Foo(), entonces el assignment.value es una instancia de ast.Call. si es un literal (por ejemplo, una lista), entonces value será esa lista y la fianza porque el objeto que me interesa no se está asignando a un nombre.

¿Cuál es la mejor manera de confirmar que assignment.value.func es, de hecho, type(obj).__call__ del objeto que me interesa. Estoy bastante seguro de que es garantizado que es "en alguna parte" o el código ni siquiera estaría funcionando. Solo necesito que esté en el nivel superior. Lo más obvio es caminar y asegurarse de que no contenga llamadas interiores. Entonces estoy garantizado que tengo el nombre. (Mi razonamiento es correcto, no estoy seguro de si sus suposiciones son). Esto no es ideal, porque si estoy interesado en Foo, esto podría llevarme a descartar a = Foo(Bar()) porque no sé si es a = Bar(Foo()).

Por supuesto que puedo comprobar assignment.value.func.id pero entonces alguien podría haber hecho Foobar = Foo o algo así que no quiero depender de esto también en gran medida

Cualquier ayuda sería muy apreciada. Como siempre, estoy interesado en cualquier otra sugerencia o problema que pueda estar pasando por alto.

Además, estoy realmente sorprendido de que tuve que inventar la etiqueta 'python-internal'.

+0

+ many - ¡Python es genial! – katrielalex

Respuesta

2

El AST no puede darle esa respuesta. Intenta usar frame.f_lasti y luego echa un vistazo al bytecode. Si la línea siguiente no es STORE_FAST, tiene llamadas internas u otra cosa que no sea la simple tarea que está buscando.

def f(): 
    f = sys._getframe() 
    i = f.f_lasti + 3 # capture current point of execution, advance to expected store 
    print dis.disco(f.f_code, i) 
0

no sé de cuánta ayuda esto es, pero ¿ha considerado el uso de la llamada a locals()? Devuelve dict que contiene el nombre y el valor de todas las variables locales.

Por ejemplo:

s = '' 
locals() 
>>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', '__name__': '__main__', '__doc__': None} 
t = s # I think this is what is of most importance to you 
locals() 
>>> {'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 's': '', 't': '', '__name__': '__main__', '__doc__': None} 

por lo que podría atravesar este diccionario y comprobar qué variables tienen (como su valor) un objeto del tipo que usted está buscando.

Como dije, no sé de cuánta ayuda puede ser esta respuesta, pero si necesita aclaración sobre algo, deje un comentario e intentaré responder de la mejor manera posible.

+0

Esto no funciona porque 'locals()' siempre se refiere al fotograma al que se llama, estoy buscando un fotograma arriba. Puedo conseguirlo con 'sys._getframe()' para 'inspect.currentfrmae'. El problema es que 'foo = bar()' no crea una entrada en 'locals()' (refiriéndose al marco en el que se realiza la asignación) hasta que _después_ 'barra .__ init __()' regresa. Pero ese es el lugar apropiado para obtener el nombre porque puedo hacerlo con un decorador en 'bar' en lugar de poner código después de cada asignación. – aaronasterling

+0

@ AaronMcSmooth: ¿Sería una llamada a globals() en lugar de locales solucionar este problema – inspectorG4dget

+1

@ InspectoG4det No por el mismo motivo. 'globals' es simplemente' frame.f_locals' para el marco más externo y, por lo tanto, una entrada no se coloca en 'frame.f_locals' hasta que el cuadro interno (' bar .__ init __() 'en este caso) retorna. – aaronasterling

0

No realizo ningún error al hacer clic aquí porque (1) esto es solo para depuración/creación de perfiles/piratería (2) este proceso exacto se completó o el código no se ejecutará. ¿Hay algún problema con esto?

Sí:

  1. iniciar un programa

  2. unidad de esperar a que importa un módulo en particular foo.py

  3. Editar foo.py

Ahora código que está cargado en un Python proceso no coincide con el código encontrado en el disco.

Otra razón más por la que desmontar el bytecode puede ser una mejor técnica.

0

Así es como se hace. Muchas gracias al dador de pistas anónimo. Mucha suerte en tu búsqueda para acumular representantes para tu cuenta alternativa.

import inspect 
import opcode 


def get_name(f): 
    """Gets the name that the return value of a function is 
    assigned to. 

    This could be modified for classes as well. This is a 
    basic version for illustration that only prints out 
    the assignment instead of trying to do anything with it. 
    A more flexible way would be to pass it a callback for when 
    it identified an assignment. 

    It does nothing for assignment to attributes. The solution 
    for that isn't much more complicated though. If the 
    instruction after the function call is a a `LOAD_GLOBAL`, 
    `LOAD_FAST` or `LOAD_DEREF`, then it should be followed by 
    a chain of `LOAD_ATTR`'s. The last one is the attribute 
    assigned to. 
    """ 

    def inner(*args, **kwargs): 
     name = None 

     frame = inspect.currentframe().f_back 
     i = frame.f_lasti + 3 

     # get the name if it exists 
     code = frame.f_code 
     instr = ord(code.co_code[i]) 
     arg = ord(code.co_code[i+1]) # no extended arg here. 
     if instr == opcode.opmap['STORE_FAST']: 
      name = code.co_varnames[arg] 
     elif instr in (opcode.opmap['STORE_GLOBAL'], 
         opcode.opmap['STORE_NAME']): 
      name = code.co_names[arg] 
     elif instr == opcode.opmap['STORE_DEREF']: 
      try: 
       name = code.co_cellvars[arg] 
      except IndexError: 
       name = code.co_freevars[arg - len(code.co_cellvars)] 
     ret = f(*args, **kwargs) 
     print opcode.opname[instr] 
     if name: 
      print "{0} = {1}".format(name, ret) 
     return ret 

    return inner 


@get_name 
def square(x): 
    return x**2 

def test_local(): 
    x = square(2) 

def test_deref(): 
    x = square(2) 
    def closure(): 
     y = x 
    return closure 

x = square(2) 
test_local() 
test_deref()() 

No debería ser demasiado difícil de averiguar las asignaciones del list_[i] = foo() para cualquiera, incluyendo el valor de i utilizando frame.f_locals. Los tramposos van a ser literales y cuando se pasa como un argumento. Ambos casos deberían ser bastante desafiantes.

Cuestiones relacionadas