2009-07-08 13 views
64

Supongamos myapp/foo.py contiene:Obtener __name__ de llamar módulo de función en Python

def info(msg): 
    caller_name = ???? 
    print '[%s] %s' % (caller_name, msg) 

Y myapp/bar.py contiene:

import foo 
foo.info('Hello') # => [myapp.bar] Hello 

Quiero caller_name se establezca en el atributo __name__ del módulo de las funciones de llamada (que es 'myapp.foo') en este caso. ¿Cómo puede hacerse esto?

+0

Supongamos que algún otro script de punto de entrada invoca bar.py .. y por lo tanto 'caller_name' no puede ser' __main__' –

+1

¿Está seguro de que su código de ejemplo es correcto? ¿No debería el cuerpo de myapp/bar.py be: import foo; foo.info ('Hola') # => [myapp.bar] Hola –

+0

Correcto, arreglé el código. –

Respuesta

91

Salida inspeccionar el módulo:

inspect.stack() devolverá la información de la pila.

Dentro de una función, inspect.stack()[1] devolverá la pila de su llamante. A partir de ahí, se puede obtener más información acerca de nombre de la función de la persona que llama, módulo, etc.

Consulte la documentación para más detalles:

http://docs.python.org/library/inspect.html

Además, Doug Hellmann tiene una buena valoración crítica del módulo de inspección en su serie PyMOTW:

http://pymotw.com/2/inspect/index.html#module-inspect

EDIT: Aquí hay algo de código que hace lo que quiere, pienso:

def info(msg): 
    frm = inspect.stack()[1] 
    mod = inspect.getmodule(frm[0]) 
    print '[%s] %s' % (mod.__name__, msg) 
+1

Entonces, ¿cómo se obtiene el atributo '__name__' de este módulo usando el módulo' inspeccionar'? Por ejemplo, ¿cómo recupero 'myapp.foo' (no' myapp/foo.py') en mi ejemplo anterior? Ya intenté usar el módulo de inspección antes de publicar en SO. –

+0

Acabo de actualizar la respuesta. ¿Eso funciona de tu lado? – ars

+6

Tenga en cuenta que esto interactuará de forma extraña con los enlaces de importación, no funcionará en ironpython y puede comportarse de forma sorprendente en jython.Lo mejor es que puedas evitar la magia de esta manera. – Glyph

2

No recomiendo hacer esto, pero se puede lograr su objetivo con el siguiente método:

def caller_name(): 
    frame=inspect.currentframe() 
    frame=frame.f_back.f_back 
    code=frame.f_code 
    return code.co_filename 

luego actualizar su método existente de la siguiente manera:

def info(msg): 
    caller = caller_name() 
    print '[%s] %s' % (caller, msg) 
+6

Nombre de archivo no es lo mismo que '__name__' –

16

Confrontado con un similares problema, he encontrado que sys._current_frames() del módulo sys contiene información interesante que puede ayudarlo, sin la necesidad de importar inspeccionar, al menos en casos de uso específicos.

>>> sys._current_frames() 
{4052: <frame object at 0x03200C98>} 

A continuación, puede "moverse hacia arriba" usando f_back:

>>> f = sys._current_frames().values()[0] 

>>> print f.f_back.f_globals['__file__'] 
'/base/data/home/apps/apricot/1.6456165165151/caller.py' 

>>> print f.f_back.f_globals['__name__'] 
'__main__' 

Para el nombre del archivo también se puede utilizar f.f_back.f_code.co_filename, según lo sugerido por Mark Roddy anteriormente. No estoy seguro de los límites y advertencias de este método (es probable que haya varios problemas) pero tengo la intención de usarlo en mi caso.

+1

nota: el código inspect.stack falla después de compilar a exe usando pyinstaller, pero utilizando sys._current_frames funciona bien ... así que esta es la técnica preferida para mí. – panofish

+5

Creo que es más fácil obtener el cuadro anterior por ['sys._getframe (1)'] (https://docs.python.org/3/library/sys.html#sys._getframe), en lugar de llamar a 'sys. _current_frames() '(btw que devuelve un mapeo de marcos para cada hilo). – hooblei

+0

Gracias hooblei, todavía no lo he probado, pero parece muy útil para situaciones de subprocesos múltiples. –

Cuestiones relacionadas