为什么在赋值给sys.modules[__name__]后__name__的值会改变?

21 投票
2 回答
10007 浏览
提问于 2025-04-16 14:03

在尝试做一些类似于ActiveState的食谱中提到的内容时,标题为Python中的常量,作者是Alex Martelli,我遇到了一个意想不到的副作用(在Python 2.7中)。这个副作用是,当我把一个类的实例赋值给sys.modules中的某个条目时,__name__的值似乎变成了None。下面的代码片段展示了这个问题,这导致食谱中的部分代码无法正常工作:

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"

我想了解为什么会发生这种情况。我不相信在Python 2.6及更早的版本中会这样,因为我有一些旧代码,似乎在赋值后if __name__ == '__main__':的条件判断是正常工作的(但现在不再如此)。

顺便提一下,我还注意到,名称_test在赋值后也从一个类对象变成了None。我觉得它们被重新绑定到None而不是完全消失,这有点奇怪……

更新:

我想补充一下,任何关于如何实现if __name__ == '__main__':效果的解决方法,考虑到发生的事情,我将非常感激。谢谢!

2 个回答

-2

如果我给 sys.modules['__main__'] 赋值什么东西,我的环境就会变得非常糟糕。虽然不是这种具体的情况,但我的全局变量和内置函数都会消失。

sys.modules 的使用并没有详细的文档说明,写入它时并没有特别的行为,只是模糊地提到可以用它来做“重新加载的技巧”(不过即使这样使用也有一些很大的陷阱)。

我不会随便往里面写非模块的东西,也不指望会有什么好结果。我觉得这个做法完全是错误的。

35

这个问题发生的原因是,你在执行 sys.modules[__name__] = _test() 这行代码时,覆盖了你的模块。这样一来,你的模块就被删除了,因为没有其他地方再引用它了,引用计数变成了零,所以它就被清理掉了。不过,解释器仍然保留着字节码,所以它还是能工作,但会把你模块里的每个变量都返回 None(这是因为当模块被删除时,Python 会把所有变量都设置为 None)。

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.

补充:

解决这个问题的方法是这样做:

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'>

希望这样能让你更明白。

撰写回答