线程本地Python打印

时间:2015-05-21 09:03:34

标签: python python-multithreading

是否有任何内置方法让不同的主题具有print()和类似的不同目的地?

我正在探索创建交互式Python环境,因此我无法使用模块spamegg中的print()。它必须是全球可用的,没有参数。

3 个答案:

答案 0 :(得分:1)

您可以将sys.stdout替换为检查当前线程并写入相应文件的对象:

import sys, threading

class CustomOutput(object):
    def __init__(self):
        # the "softspace" slot is used internally by Python's print
        # to keep track of whether to prepend space to the
        # printed expression
        self.softspace = 0
        self._old_stdout = None

    def activate(self):
        self._old_stdout = sys.stdout
        sys.stdout = self

    def deactivate(self):
        sys.stdout = self._old_stdout
        self._old_stdout = None

    def write(self, s):
        # actually write to an open file obtained from an attribute
        # on the current thread
        threading.current_thread().open_file.write(s)

    def writelines(self, seq):
        for s in seq:
            self.write(s)

    def close(self):
        pass

    def flush(self):
        pass

    def isatty(self):
        return False

答案 1 :(得分:0)

可以做你要求的事情,虽然它很复杂,很笨重,也可能不便携,我认为这不是你想做的事。

您对使用spamegg.print的反对意见是:

  

我正在探索创建交互式Python环境,因此我不能只使用模块print()中的spamegg。它必须是全球可用的,没有参数。

但解决方法很简单:只需在代码中使用模块print中的spamegg,在交互式解释器中使用from spamegg import print即可。这就是它的全部内容。

就此而言,没有任何理由甚至需要首先调用print。如果您的所有代码都使用了其他名称不同的输出函数,那么您可以在交互式解释器中执行相同的操作。

但是,这又如何让每个线程都有不同的目的地?

这样做的简单方法是在threading.local()中查找目的地。

但如果你真的想要这么做的两个部分,你可以。

要以艰难的方式完成全局print,您可以spamegg替换内置print,而不是仅仅为您提供隐藏它的方法,或者让它替换sys.stdout 1}},所以带有默认参数的内置print将打印在其他地方。

import builtins
_real_print = builtins.print
def _print(*args, **kwargs):
    kwargs.setdefault('file', my_output_destination)
    _real_print(*args, **kwargs)
builtins.print = _print

import io
import sys
class MyStdOut(io.TextIOBase):
    # ... __init__, write, etc.
sys.stdout = MyStdOut()

仍需要MyStdOut使用线程本地目标。

或者,您可以在自己的自定义globals环境中编译或打包每个线程函数,从而将__builtins__和/或sys替换为默认值,允许您为其提供另一个每个线程从一开始。例如:

from functools import partial
from threading import Thread
from types import FunctionType

class MyThread(Thread):
    def __init__(self, group=None, target=None, *args, **kwargs):
        if target:
            g = target.__globals__.copy()
            g['__builtins__'] = g['__builtins__'].copy()
            output = make_output_for_new_thread()
            g['__builtins__']['print'] = partial(print, file=output)
            target = FunctionType(thread_func.__code__, g, thread_func.__name__, 
                                  thread_func.__defaults__, thread_func.__closure__)
        super().__init__(self, group, target, *args, **kwargs) 

答案 2 :(得分:-1)

我可能会为您提供解决方案,但它比打印要复杂得多。

class ClusteredLogging(object):
    '''
    Class gathers all logs performed inside with statement and flush
    it to mainHandler on exit at once.
    Good for multithreaded applications that has to log several
    lines at once.
    '''


    def __init__(self, mainHandler, formatter):
        self.mainHandler = mainHandler
        self.formatter = formatter

        self.buffer = StringIO()
        self.handler = logging.StreamHandler(self.buffer)
        self.handler.setFormatter(formatter)


    def __enter__(self):
        rootLogger = logging.getLogger()
        rootLogger.addHandler(self.handler)


    def __exit__(self, t, value, tb):
        rootLogger = logging.getLogger()
        rootLogger.removeHandler(self.handler)
        self.handler.flush()
        self.buffer.flush()

        rootLogger.addHandler(self.mainHandler)
        logging.info(self.buffer.getvalue().strip())
        rootLogger.removeHandler(self.mainHandler)

使用此方法,您可以为每个线程创建日志处理程序,并将其配置为将日志存储到不同的位置。

请记住,这是以略微不同的目标开发的(请参阅注释),但您可以通过将ClusteredLogging的处理程序杂耍功能作为开头进行调整。

还有一些测试代码:

import concurrent.futures
try:
    from StringIO import StringIO
except ImportError:
    from io import StringIO
import logging
import sys

# put ClusteredLogging here

if __name__ == "__main__":
    formatter = logging.Formatter('%(asctime)s %(levelname)8s\t%(message)s')
    onlyMessageFormatter = logging.Formatter("%(message)s")
    mainHandler = logging.StreamHandler(sys.stdout)
    mainHandler.setFormatter(onlyMessageFormatter)
    mainHandler.setLevel(logging.DEBUG)
    rootLogger = logging.getLogger()
    rootLogger.setLevel(logging.DEBUG)

    def logSomethingLong(label):
        with ClusteredLogging(mainHandler, formatter):
            for i in range(15):
                logging.info(label + " " + str(i))        

    labels = ("TEST", "EXPERIMENT", "TRIAL")

    executor = concurrent.futures.ProcessPoolExecutor()
    futures = [executor.submit(logSomethingLong, label) for label in labels]
    concurrent.futures.wait(futures)