我有一个上下文管理器,它将输出捕获到一个字符串中,用于在with
语句下缩进的代码块。此上下文管理器生成一个自定义结果对象,当块执行完毕后,该对象将包含捕获的输出。
from contextlib import contextmanager
@contextmanager
def capturing():
"Captures output within a 'with' block."
from cStringIO import StringIO
class result(object):
def __init__(self):
self._result = None
def __str__(self):
return self._result
try:
stringio = StringIO()
out, err, sys.stdout, sys.stderr = sys.stdout, sys.stderr, stringio, stringio
output = result()
yield output
finally:
output._result, sys.stdout, sys.stderr = stringio.getvalue(), out, err
stringio.close()
with capturing() as text:
print "foo bar baz",
print str(text) # prints "foo bar baz"
当然,我不能只返回一个字符串,因为字符串是不可变的,因此用户从with
语句返回的字符串在其代码块运行后无法更改。但是,在使用str
之后必须显式地将结果对象转换为字符串是一种拖累(我还使用了使对象可调用为一点语法糖)。
因此可以使结果实例像字符串一样,因为它确实在命名时返回一个字符串吗?我尝试实现__get__
,但这似乎只适用于属性。或者我真的不想做什么?
答案 0 :(得分:4)
如何创建一个类似字符串的类? 子类 str
import os
class LikeAStr(str):
'''Making a class like a str object; or more precisely
making a str subclass with added contextmanager functionality.'''
def __init__(self, diff_directory):
self._iwd = os.getcwd()
self._cwd = diff_directory
def __enter__(self):
return self
def __exit__(self, ext_typ, exc_value, traceback):
try: os.chdir(self._iwd) # might get deleted within the "with" statement
except: pass
def __str__(self):
return self._cwd
def __repr__(self):
return repr(self._cwd)
astr = LikeAStr('C:\\')
with LikeAStr('C:\\') as astr:
print 1, os.getcwd()
os.chdir( astr ) # expects str() or unicode() not some other class
print 2, os.getcwd()
#
# out of with block
print 3, os.getcwd()
print 4, astr == 'C:\\'
输出:
1 D:\Projects\Python\
2 C:\
3 D:\Projects\Python\
4 True
答案 1 :(得分:2)
我不相信有一种干净的方式来做你想做的事。
text
在模块'globals()
dict中定义。
您必须在capturing
对象中修改此globals()dict:
如果您尝试在函数中使用with
,则下面的代码会中断,因为那时text
将位于函数的范围内,而不是全局变量。
import sys
import cStringIO
class capturing(object):
def __init__(self,varname):
self.varname=varname
def __enter__(self):
self.stringio=cStringIO.StringIO()
self.out, sys.stdout = sys.stdout, self.stringio
self.err, sys.stderr = sys.stderr, self.stringio
return self
def __exit__(self,ext_type,exc_value,traceback):
sys.stdout = self.out
sys.stderr = self.err
self._result = self.stringio.getvalue()
globals()[self.varname]=self._result
def __str__(self):
return self._result
with capturing('text') as text:
print("foo bar baz")
print(text) # prints "foo bar baz"
# foo bar baz
print(repr(text))
# 'foo bar baz\n'
答案 2 :(得分:2)
乍一看,它看起来像UserString
(实际上是MutableString
,但在Python 3.0中消失了)基本上就是我想要的。不幸的是,UserString不像那样就像一个字符串;我在print
语句中得到了一些奇怪的格式,这些语句以逗号结尾,与str
字符串一起运行良好。 (如果它不是一个“真正的”字符串,或者某些东西,你似乎会得到一个额外的空间。)我在玩包装字符串时创建的玩具类也有同样的问题。我没有花时间追查原因,但看起来UserString
作为一个例子最有用。
我实际上最终使用了bytearray
,因为它在大多数情况下都像字符串一样工作,但是可变。我还写了一个单独的版本splitlines()
将文本放入列表中。这很好用,对我的直接用例实际上更好,它在各种函数的连接输出中删除“额外”空行。这是那个版本:
import sys
from contextlib import contextmanager
@contextmanager
def capturinglines(output=None):
"Captures lines of output to a list."
from cStringIO import StringIO
try:
output = [] if output is None else output
stringio = StringIO()
out, err = sys.stdout, sys.stderr
sys.stdout, sys.stderr = stringio, stringio
yield output
finally:
sys.stdout, sys.stderr = out, err
output.extend(stringio.getvalue().splitlines())
stringio.close()
用法:
with capturinglines() as output:
print "foo"
print "bar"
print output
['foo', 'bar']
with capturinglines(output): # append to existing list
print "baz"
print output
['foo', 'bar', 'baz']
答案 3 :(得分:1)
我认为你可以建立这样的东西。
import StringIO
capturing = StringIO.StringIO()
print( "foo bar baz", file= capturing )
现在'foo bar baz \ n'== capturing.getvalue()
这是最简单的。除了修复print
函数以使用file=
参数外,它无需额外工作即可完美运行。
答案 4 :(得分:1)
如何创建一个类似字符串的类?
如果您不想出于某种原因继承 str :
class StrBuiltin(object):
def __init__(self, astr=''):
self._str = astr
def __enter__(self):
return self
def __exit__(self, ext_typ, exc_value, traceback):
pass # do stuff
def __str__(self):
return self._str
def __repr__(self):
return repr(self._str)
def __eq__(self, lvalue):
return lvalue == self._str
def str(self):
'''pretend to "convert to a str"'''
return self._str
astr = StrBuiltin('Eggs&spam')
if isinstance( astr.str(), str):
print 'Is like a str.'
else:
print 'Is not like a str.'
我知道你不想做str(MyClass)但MyClass.str()有点暗示,对我来说,这个类应该把它自己暴露为str的函数,这些函数期望str作为一部分宾语。而不是一些意外的结果“谁知道str将返回什么(SomeObject)。