【问题标题】:Scope of Global variables in Python in WindowsWindows中Python中全局变量的范围
【发布时间】:2015-04-23 22:28:15
【问题描述】:

我有一个 CLI 脚本,用于将文件推送到 s3 存储桶中。 对于较大的文件,我将文件分成几部分并上传 在平行下。 (在这里粘贴代码结构。我试图做一个极简的例子,但即使这样也有 60 行)

def _upload_part(argFile, argBucket, max_attempts_limit, **core_chunk):
    #bunch of stuff
    pool = Pool(processes=parallel_processes)
    for i in range( chunk_amount ):
        #bunch of stuff
        pool.apply_async( _upload_for_multipart, [keyname, offset, mp, part_num, bytes] )
    pool.close()
    pool.join()

def _upload_for_multipart(keyname, offset, mp, part_num, bytes):
    #code to upload each part
    #log the status of each part to log files

def _get_logger( pdir, ldir, lname, level, fmt ):
    os.makedirs( logs_dir )
    logging.basicConfig(
        filename=os.path.join(logs_dir, lname),
        level=level,
        format=fmt
    )
    return logging.getLogger( lname )

#under main
if __name__ == "__main__":
    logneeds = dict( pdir=exec_dir, ldir='logs', lname='s3_cli.log', level='INFO',
                               fmt='%(asctime)s %(levelname)s: %(message)s' )
    logger = _get_logger(**logneeds)

上述代码结构在 OSX 和 Linux 中有效,但在 Windows 中失败。它说 name 'logger' is not defined_upload_for_multipart函数。全局变量的方式有区别吗 在基于 windows 和 unix 的操作系统中进行解释?

编辑:添加了工作示例here

【问题讨论】:

  • 很难调试不存在的代码。
  • @martineau - 我可以在这里发布代码,但我不能让它小于 60 行代码......
  • 好吧,您至少应该包含导致name 'logger' is not defined 错误的行的部分。有些人将较长的代码片段上传到 pastebinGitHub 之类的网站上,并在他们的问题中包含对它的 url 引用。
  • @martineau - 在 SO AFAIK 上不允许粘贴指向代码的链接并被否决。如果代码太长,人们会要求一个小例子
  • 不鼓励但人们还是这样做(粘贴链接)。

标签: python logging multiprocessing


【解决方案1】:

您看到的是在 Windows 与 UNIX-y 操作系统(或任何支持 fork 的操作系统)上创建子进程的方式有所不同。在 Linux 上,fork 用于创建子进程,它在父进程的写时复制副本中创建子进程。这意味着父级中存在的所有变量都将存在于子级中。

但是,Windows 不支持fork。为了创建子进程,它产生了一个全新的进程,然后在子进程中重新导入父进程的__main__ 模块。在你的情况下,这意味着运行你的程序的脚本被导入到子进程中。 logger 的创建发生在父级的 if __name__ == "__main__": 守卫内,这意味着当子级导入模块时它会被跳过。 multiprocessing 文档中有几个地方提到了这一点,我在 this answer 中明确列出。

为了使logger 存在于子模块中,它需要在模块的顶层创建(这意味着它每次导入时都会运行),或者您需要在其中显式创建它所有子进程,也许使用multiprocessing.Poolinitializer 参数:

logger = None

def init_log():
    global logger
    logneeds = dict(pdir=exec_dir, ldir='logs', lname='s3_cli.log', level='INFO',
                    fmt='%(asctime)s %(levelname)s: %(message)s' )
    logger = _get_logger(**logneeds)

def _upload_part(argFile, argBucket, max_attempts_limit, **core_chunk):
    #bunch of stuff
    pool = Pool(processes=parallel_processes, initializer=init_log)
    for i in range( chunk_amount ):
        #bunch of stuff
        pool.apply_async( _upload_for_multipart, [keyname, offset, mp, part_num, bytes] )
    pool.close()
    pool.join()

def _upload_for_multipart(keyname, offset, mp, part_num, bytes):
    #code to upload each part
    #log the status of each part to log files

def _get_logger( pdir, ldir, lname, level, fmt ):
    os.makedirs( logs_dir )
    logging.basicConfig(
        filename=os.path.join(logs_dir, lname),
        level=level,
        format=fmt
    )
    return logging.getLogger( lname )

#under main
if __name__ == "__main__":
    init_log()

【讨论】:

  • 感谢您的解释。我遵循了您的建议和您给定的代码,并对我的代码进行了必要的更改。然而子进程抛出一个错误说 - Nonetype object has no attribute info
  • @Beginner 您能否编辑您的问题以包含重现该问题的完整示例?
  • - 去掉 80% 的代码后,我添加了一个脚本链接。
  • @Beginner 问题出在这里:Pool(processes=parallel_processes, initializer=init_log(logFile))。您正在使用initializer=init_log(logFile),它实际上在父进程中调用init_log,并将该函数的返回值传递给initializer。这意味着 logger 根本没有在孩子中设置。您应该做的只是将init_log 函数传递给子进程,以便logger 实际上是在子进程中设置的。所以,你应该这样做:Pool(processes=parallel_processes, initializer=init_log, initargs=(logFile,))
  • 我感觉通过initializer = ini_log(logFile) 是不对的。我解决了这些问题。现在我遇到了permissionError。我认为这是向前迈出的一步
猜你喜欢
  • 1970-01-01
  • 2016-11-13
  • 2012-01-19
  • 1970-01-01
  • 2013-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-29
相关资源
最近更新 更多