Self Modifying Python? How can I redirect all print statements within a function without touching sys.stdout?

核能气质少年 提交于 2020-01-06 21:06:17

问题


I have a situation where I am attempting to port some big, complex python routines to a threaded environment.

I want to be able to, on a per-call basis, redirect the output from the function's print statement somewhere else (a logging.Logger to be specific).

I really don't want to modify the source for the code I am compiling, because I need to maintain backwards compatibility with other software that calls these modules (which is single threaded, and captures output by simply grabbing everything written to sys.stdout).

I know the best option is to do some rewriting, but I really don't have a choice here.

Edit -

Alternatively, is there any way I can override the local definition of print to point to a different function?

I could then define the local print = system print unless overwritten by a kwarg, and would only involve modify a few lines at the beginning of each routine.


回答1:


In Python2.6 (and 2.7), you can use

from __future__ import print_function

Then you can change the code to use the print() function as you would for Python3

This allows you to create a module global or local function called print which will be used in preference to the builtin function

eg.

from __future__ import print_function

def f(x, print=print):
    print(x*x)

f(5)
L=[]
f(6, print=L.append)
print(L)



回答2:


Modifying the source code doesn't need to imply breaking backward compatibility.

What you need to do is first replace every print statement with a call to a function that does the same thing:

import sys
def _print(*args, **kw):
    sep = kw.get('sep', ' ')
    end = kw.get('end', '\n')
    file = kw.get('file', sys.stdout)
    file.write(sep.join(args))
    file.write(end)

def foo():
   # print "whatever","you","want"
   _print("whatever","you","want")

Then the second step is to stop using the _print function directly and make it a keyword argument:

def foo(_print=_print):
    ...

and make sure to change all internal function calls to pass the _print function around.

Now all the existing code will continue to work and will use print, but you can pass in whatever _print function you want.

Note that the signature of _print is exactly that of the print function in more recent versions of Python, so as soon as you upgrade you can just change it to use print(). Also you may get away with using 2to3 to migrate the print statements in the existing code which should reduce the editing required.




回答3:


Someone in the sixties had an idea about how to solve this but it requires a bit of alien technology. Unfortunately python has no "current environment" concept and this means you cannot provide context unless specifying it in calls as a parameter.

For handling just this specific problem what about replacing stdout with a file-like object that behaves depending on a thread-specific context ? This way the source code remains the same but for example you can get a separate log for each thread. It's even easy to do this on a specific per-call way... for example:

class MyFakeStdout:
    def write(self, s):
        try:
            separate_logs[current_thread()].write(s)
        except KeyError:
            old_stdout.write(s)

and then having a function to set a logger locally to a call (with)

PS: I saw the "without touching stdout" in the title but I thought this was because you wanted only some thread to be affected. Touching it while still allowing other threads to work unaffected seems to me compatible with the question.



来源:https://stackoverflow.com/questions/4573727/self-modifying-python-how-can-i-redirect-all-print-statements-within-a-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!