import logging.handlers 破坏了我的程序。为什么?

import logging.handlers breaks my program. Why?

在调试问题时,我已经将有问题的程序简化为只有几行,但我仍然不明白哪里出了问题。你能帮忙吗?

import logging

def setup():
    logging.warning("start")
    import logging.handlers

setup()

上面的代码产生了一个异常:

logging.warning("start")

UnboundLocalError: local variable 'logging' referenced before assignment

并且 pylint 抱怨:

W: 5, 4: Redefining name 'logging' from outer scope (line 1) (redefined-outer-name)

这个修改似乎有帮助,但我不知道为什么:

import logging.handlers as lh

请注意,异常是在执行第二个导入语句之前抛出的。我很困惑。

logging.warning 函数将首先尝试在 setup() 函数中引用局部作用域的变量,该函数来自 import logging.handlers,它发现它在你的行之后声明正在使用它。

尝试将其更改为:

import logging

def setup():
    global logging
    logging.warning("start")
    import logging.handlers

setup()

如果反汇编该函数的字节码,您会发现问题所在:

>>> dis.dis(setup)
  2           0 LOAD_FAST                0 (logging)
              3 LOAD_ATTR                0 (warning)
              6 LOAD_CONST               1 ('start')
              9 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             12 POP_TOP

  3          13 LOAD_CONST               2 (0)
             16 LOAD_CONST               0 (None)
             19 IMPORT_NAME              1 (logging.handlers)
             22 STORE_FAST               0 (logging)
             25 LOAD_CONST               0 (None)
             28 RETURN_VALUE

请注意代码如何导入名称 logging.handlers,然后 将模块对象 分配给名称 logging.

所以基本上就像你的函数是:

def setup():
    logging.warning('start')
    logging = __import__('logging.handlers')

或者,删除导入机制:

x = 0
def setup():
    # inside this function *all* x refer to the same object
    print(x)
    x = 1

请记住,给定范围内的名称只能引用一个对象。通过分配,编译器将 logging 解释为整个范围的 local 变量 ,这是完整的函数体 ,因此调用 warning 会产生一个 UnboundLocalError,因为对 logging 的赋值是在之后完成的。

您应该将导入作为函数中的第一条语句移动,或者只是将其移动到全局范围内(这是执行此操作的首选方式)。


如果你想强制编译器引用全局变量,你必须通过添加 global logging/global x 语句来告诉他。在这种情况下,不会创建局部变量,但会使用全局变量。