2

as an example here, i want to make a function to temporarily direct the stdout to a log file.

the tricky thing is that the codes have to keep the file handler and std sav for restoration after the redirect, i wrote it in class type to keep these two variables.

here below the full code:

class STDOUT2file:
 def __init__(self,prefix='report@'):
 now=dt.date.today()
 repname=repnameprefix=prefix+now.strftime("%Y%m%d")+'.txt'
 count=0
 while os.path.isfile(repname):
 count+=1
 repname=repnameprefix+(".%02d" %(count))
 self.sav=sys.stdout
 f=open(repname,'w')
 sys.stdout=f
 self.fname=repname
 self.fhr=f
 def off(self,msg=False):
 sys.stdout=self.sav
 self.fhr.close()
 if msg: 
 print('output to:'+self.fname)
 return

here is the code to apply it:

outbuf=STDOUT2file()
#codes to print out to stdout
outbuf.off(msg=True)

i want to make it more clean, read about 'closure' but it returns a function at the first call, kind of assigment type as similar as class.

i want it to be like:

STDOUT2file('on')

STDout2file('off',msg=True)

note: redirecting to stdout is an example i encountered just now.. what i am wondering is, any way other than class type to make simple functionality like those on/off type, which involve store/retrieval of state variables that should be better made invisible to outside.

asked Mar 21, 2014 at 1:54

2 Answers 2

3

Try using a context manager instead. This idiom is common enough that it was included in the PEP that introduced context managers (slightly modified here):

from contextlib import contextmanager
@contextmanager
def redirect_stdout(new_stdout):
 import sys
 save_stdout = sys.stdout
 sys.stdout = new_stdout
 try:
 yield
 finally:
 sys.stdout = save_stdout

Or, if you like, the class-based version with __enter__ and __exit__:

class redirect_stdout:
 """Context manager for temporarily redirecting stdout to another file
 docstring truncated
 """
 def __init__(self, new_target):
 self.new_target = new_target
 def __enter__(self):
 self.old_target = sys.stdout
 sys.stdout = self.new_target
 return self.new_target
 def __exit__(self, exctype, excinst, exctb):
 sys.stdout = self.old_target

Raymond Hettinger actually committed this to contextlib, it will be included in python 3.4 as contextlib.redirect_stdout().

Basic usage:

with open('somelogfile','a') as f:
 with stdout_redirected(f):
 print(something)
answered Mar 21, 2014 at 2:13
Sign up to request clarification or add additional context in comments.

1 Comment

tks. but the redirect to stdout is just an example.. what i am asking is, if i want to do some functionalities that involve storing of certain states variable that would be retrieved later, and i want to encapsulate (or make it invisible) these variable, could i do it other than class type, which for some simple functions, like those on/off type, is an overkill.
1

Yes, you can save state information in a function. Just name the variable functionname.something and it will be saved. For example:

def stdout2file(status, prefix='pre', msg=False):
 import datetime as dt
 import os
 import sys
 if not hasattr(stdout2file, 'sav'):
 stdout2file.sav = None
 if status == 'on':
 if stdout2file.sav:
 print('You have already triggered this once Ignoring this request.')
 else:
 now = dt.date.today()
 repname = repnameprefix = prefix + now.strftime("%Y%m%d") + '.txt'
 count = 0
 while os.path.isfile(repname):
 count += 1
 repname = repnameprefix + (".%02d" %(count))
 stdout2file.sav = sys.stdout
 f = open(repname,'w')
 sys.stdout = f
 stdout2file.fhr = f
 elif status == 'off':
 if not stdout2file.sav:
 print('Redirect is "off" already. Ignoring this request')
 else:
 sys.stdout = stdout2file.sav
 stdout2file.fhr.close()
 if msg:
 print('output to:' + stdout2file.fhr.name)
 stdout2file.sav = None
 else:
 print('Unrecognized request')

It is also possible to keep status information in mutable keyword parameters like so:

def stdout_toggle(prefix='pre', msg=False, _s=[None, None]):
 import datetime as dt
 import os
 import sys
 if _s[0] is None:
 now = dt.date.today()
 repname = repnameprefix = prefix + now.strftime("%Y%m%d") + '.txt'
 count = 0
 while os.path.isfile(repname):
 count += 1
 repname = repnameprefix + (".%02d" %(count))
 f = open(repname,'w')
 _s[:] = [sys.stdout, f]
 sys.stdout = f
 else:
 sys.stdout = _s[0]
 _s[1].close()
 if msg:
 print('output to:' + _s[1].name)
 _s[:] = [None, None]

The user can call the above without any arguments and it will toggle between the redirect between on and off. The function remembers the current status through the keyword parameter _s which is a mutable list.

Although some consider the fact that mutable keyword parameters are preserved between function calls to be a language flaw, it is consistent with python philosophy. It works because the default values for keyword parameters are assigned when the function is first defined, that is when the def statement is executed, and not when the function is called. Consequently, _s=[None, None] is assigned once at definition and is free to vary thereafter.

answered Mar 21, 2014 at 5:16

8 Comments

your code works! btw, how to avoid the redirection was triggered twice? checking stdout2file.sav is none didn't work, as it is 'reference before it is defined', tkyou!
@timeislove OK. I added checking so that, if there are two "on" or two "off" requests in a row, only the first will be processed. If also will refuse to turn "off" if not first turned "on".
though the conversion works, it looks like more lines of codes than i expected, more than that of using class type... hmm...
What are you talking about? It should be the same number of lines as with the class version, since the only change is self.save into stdout2file.sav.
@timeislove There were some superfluous lines and I removed them. For example, there was no need to keep stdout2file.fname around since the same information is in stdout2file.fhr.name. Note also that I included three import statements in the function. The class needs the same imports but they are not shown in the class definition.
|

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.