导入此子模块如何覆盖父__init__.py模块中的值?

时间:2015-10-07 22:50:06

标签: python

我有package with three files

testimport
├── __init__.py
├── logging.py
└── util.py

__init__.py包含:

from __future__ import ( absolute_import, division, print_function, unicode_literals )
import logging # imports standard library module (because absolute_import is activated)
_logging_file = logging.__file__
from .util import testlog
if _logging_file != logging.__file__:
    # at this point, `logging` no longer points to the standard
    # library module, but the local logging module instead(!)
    raise AssertionError('`logging` overwritten; {!r} is not equal to {!r}'.format(_logging_file, logging.__file__))
LOGGER = logging.getLogger(__name__)

logging.py包含:

import sys
__all__ = ()
SILENT = -(sys.maxsize) - 1

util.py包含:

from __future__ import ( absolute_import, division, print_function, unicode_literals )
import logging # imports standard library module (because absolute_import is activated)
from .logging import SILENT # this is (perversely) where the importing module's `logging` gets overridden
__all__ = ( 'testlog' )
_LOGGER = logging.getLogger(__name__)
def testlog(log_lvl=SILENT):
    _LOGGER.log(log_lvl, 'Hello!')

AssertionError import时会引发testimport

% python
Python 2.7.10 (default, Sep 24 2015, 10:13:45)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import testimport
<function testlog at 0x10e86e1b8>
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "testimport/__init__.py", line ..., in <module>
    raise AssertionError('`logging` overwritten; {!r} is not equal to {!r}'.format(_logging_file, logging.__file__))
AssertionError: `logging` overwritten; '/.../lib/python2.7/logging/__init__.pyc' is not equal to 'testimport/logging.pyc'

为什么发生这种情况?

测试回购是here。 Travis构建版本为here

更新,当使用pdb单步执行此操作时,from .logging import SILENT中的违规指令似乎为util.py,但我不明白为什么。这是repo版本的缩写会话:

% echo 'import testimport' >|testme.py
% python -m pdb testme.py
(Pdb) s
--Call--
> /.../testimport/testimport/__init__.py(1)<module>()
-> from __future__ import (
(Pdb) b 12
Breakpoint 1 at /.../testimport/testimport/__init__.py:12
(Pdb) c
> /.../testimport/testimport/__init__.py(12)<module>()
-> from testimport.util import testlog
(Pdb) s
--Call--
> /.../testimport/testimport/util.py(1)<module>()
-> from __future__ import (
(Pdb) b 5
Breakpoint 2 at /.../testimport/testimport/util.py:5
(Pdb) c
> /.../testimport/testimport/util.py(5)<module>()
-> from .logging import SILENT
(Pdb) u
> /.../testimport/testimport/__init__.py(12)<module>()
-> from testimport.util import testlog
(Pdb) p logging
<module 'logging' from '/.../lib/python2.7/logging/__init__.pyc'>
(Pdb) d
> /.../testimport/testimport/util.py(5)<module>()
-> from .logging import SILENT
(Pdb) s
--Call--
> /.../testimport/testimport/logging.py(1)<module>()
-> from __future__ import (
(Pdb) b 6
Breakpoint 3 at /.../testimport/testimport/logging.py:6
(Pdb) c
> /.../testimport/testimport/logging.py(6)<module>()
-> SILENT = -(sys.maxsize) - 1
(Pdb) u
> /.../testimport/testimport/util.py(5)<module>()
-> from .logging import SILENT
(Pdb) u
> /.../testimport/testimport/__init__.py(12)<module>()
-> from testimport.util import testlog
(Pdb) p logging
<module 'logging' from '/.../lib/python2.7/logging/__init__.pyc'>
(Pdb) s
> /.../testimport/testimport/util.py(7)<module>()
-> 'testlog',
(Pdb) u
> /.../testimport/testimport/__init__.py(12)<module>()
-> from testimport.util import testlog
(Pdb) p logging
<module 'testimport.logging' from 'testimport/logging.pyc'>

1 个答案:

答案 0 :(得分:0)

经过一些更多的研究,我理解上述是正确的行为。我很尴尬地说我之前没有看到这个。一旦人们了解正在发生的事情,它就完全有道理。来自the docs

  

子模

     

当使用任何机制(例如importlib API,importimport-from语句或内置__import__())加载子模块时,会在父模块对子模块对象的命名空间。

     

...

     

鉴于Python熟悉的名称绑定规则,这似乎令人惊讶,但它实际上是导入系统的基本功能。

在Python 3.4之前似乎没有这种解释,但会影响所有版本的Python。请参阅Python issue #24029

在上面的示例中,testimport.logging必然是指本地模块。导入它(从任何地方)将其安装在testimport中(如预期的那样)。毫无疑问,这必然会取代logging的任何现有testimport成员。