Reputation: 1105
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.
Upvotes: 2
Views: 173
Reputation: 113864
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.
Upvotes: 1
Reputation: 25954
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)
Upvotes: 3