2011-03-19 18 views
15

Al intentar hacer algo similar a lo que contiene la receta ActiveState titulada Constants in Python por Alex Martelli, encontré el inesperado efecto secundario (en Python 2.7) que tiene la asignación de una instancia de clase a una entrada en sys.modules, es decir que hacerlo al parecer, cambia el valor de __name__ a None como se ilustra en el siguiente fragmento de código (que rompe parte del código en la receta):¿Por qué cambia el valor de __name__ después de la asignación a sys.modules [__ name__]?

class _test(object): pass 

import sys 
print '# __name__: %r' % __name__ 
# __name__: '__main__' 
sys.modules[__name__] = _test() 
print '# __name__: %r' % __name__ 
# __name__: None 

if __name__ == '__main__': # never executes... 
    import test 
    print "done" 

me gustaría entender por qué esto está ocurriendo. No creo que fuera así en Python 2.6 y versiones anteriores, ya que tengo un código anterior donde aparentemente el condicional if __name__ == '__main__': funcionó como se esperaba después de la asignación (pero ya no funciona).

FWIW, también noté que el nombre _test está recibiendo rebote de un objeto de clase a None, también, después de la asignación. Parece extraño para mí que están siendo rebote a None en vez de desaparecer por completo ...

Actualización:

me gustaría añadir que soluciones provisionales para lograr el efecto de if __name__ == '__main__':, teniendo en cuenta lo suceder sería muy apreciado. TIA!

Respuesta

25

Esto sucede porque ha sobrescrito su módulo cuando lo hizo sys.modules[__name__] = _test() por lo que su módulo se eliminó (porque el módulo ya no tenía referencias y el contador de referencia pasó a cero por lo que se borra) pero mientras tanto el intérprete todavía tiene el código de bytes, por lo que seguirá funcionando, pero devolviendo None a cada variable en su módulo (esto es porque python establece todas las variables en None en un módulo cuando se elimina).

class _test(object): pass 

import sys 
print sys.modules['__main__'] 
# <module '__main__' from 'test.py'> <<< the test.py is the name of this module 
sys.modules[__name__] = _test() 
# Which is the same as doing sys.modules['__main__'] = _test() but wait a 
# minute isn't sys.modules['__main__'] was referencing to this module so 
# Oops i just overwrite this module entry so this module will be deleted 
# it's like if i did: 
# 
# import test 
# __main__ = test 
# del test 
# __main__ = _test() 
# test will be deleted because the only reference for it was __main__ in 
# that point. 

print sys, __name__ 
# None, None 

import sys # i should re import sys again. 
print sys.modules['__main__'] 
# <__main__._test instance at 0x7f031fcb5488> <<< my new module reference. 

EDIT:

Una solución estará haciendo de esta manera:

class _test(object): pass 

import sys 
ref = sys.modules[__name__] # Create another reference of this module. 
sys.modules[__name__] = _test() # Now when it's overwritten it will not be 
            # deleted because a reference to it still 
            # exists. 

print __name__, _test 
# __main__ <class '__main__._test'> 

Hope esto explicará cosas.

+3

Y el comportamiento "sobrescribir todo con ninguno" proviene de algo que el destructor del módulo hace deliberadamente para borrar los ciclos de referencia entre las funciones definidas en el módulo y el módulo '__dict__'. – ncoghlan

+0

Buena respuesta y excelente solución. Nunca había considerado eso eliminar un 'sys.La referencia de módulos [] 'al módulo podría provocar su destrucción inmediata debido a que su recuento de referencias se iría a cero (especialmente si se tiene en cuenta que se está haciendo mediante código en el propio módulo). ¡Gracias! – martineau

+0

En función de su sugerencia de solución alternativa, he reemplazado el 'sys.modules [__ name__] = _test()' con un '_ref, sys.modules [__ name__] = sys.modules [__ name__], _test()' y todo parece bueno otra vez. – martineau

1

Si asigno algo a sys.modules['__main__'] obtengo un entorno gravemente roto. No es este comportamiento exacto, pero todos mis globales y edificios internos desaparecen.

sys.modules no está documentado comportarse de una manera particular cuando se escribe en él, solo vagamente que puede usarlo para "recargar trucos" (y hay algunas trampas significativas incluso para ese uso).

No escribiría ningún módulo y esperaría algo más que dolor. Creo que esta receta está totalmente equivocada.

+0

En el REPL 'sys.modules [' __ main __ '] 'refiérase al módulo integrado (' '), así que si lo sobreescribe, el built-in El módulo se eliminará para que ya no tenga acceso a ninguna función incorporada. – mouad

+3

No está mal orientado, es dulce ... por supuesto, cada vez que * escribe * en algo en el módulo sys, debe tener en cuenta que "Aquí hay dragones". ;-) –

Cuestiones relacionadas