我创建了一个python Context Manager,它捕获所有输出sys.stdout,例如print(),并将其写入文件。
问题在于我无法使用python 2.7和3.6。
上下文管理器内部使用
self.file_writer = open(self.log_file, 'w', encoding='utf8')
但是当我在Python 2.7中运行它时,
print(u"a test string")
会出现错误消息:
write() argument 1 must be unicode, not str
即使字符串显然是unicode。
如果我将文件更改为
self.file_writer = open(self.log_file, 'wb')
然后它适用于Python 2.7,但不适用于3.6。
我需要做些什么才能让它适用于任何python版本?
以下是经理的摘录:
PATH_PREFIX = "some/path/"
class manager:
def __init__(self):
self.log_file = os.path.join(PATH_PREFIX, 'log.txt')
def __enter__(self):
# create a file for logging
self.log_file_stream = open(self.log_file, 'w', encoding='utf8')
self.log_file_stream.__enter__()
# redirect stdout to this file
self.previous_stdout = sys.stdout
sys.stdout = self.log_file_stream
return self
def __exit__(self, etype, value, exception_traceback):
# stop redirecting stdout to the log file
sys.stdout = self.previous_stdout
# close the log file
self.log_file_stream.__exit__()
答案 0 :(得分:4)
sys.stdout
应该是Python 2中的字节流,但是Python 3中的Unicode流。用于Python 2的print
在写入stdout之前将Unicode字符串编码为字节字符串,但是你'已覆盖sys.stdout
在Python 2和Python 3中都是Unicode流。
当覆盖sys.stdout
时,您需要为Python 2提供字节流,但需要为Python 3提供Unicode流。您可以使用sys.version_info.major
来决定支持哪个。
答案 1 :(得分:0)
在阅读了其他答案并了解到python的现有库无法实现这一点之后,我编写了一个替换open()的上下文管理器。它有点笨拙,但它完成了工作。
可以通过显式检查python版本而不是使用hasattr()来改进它,但是当我写这篇文章时我并不知道:
class open_properly(object):
"""
This is a file writer that doesn't complain about unicode/string mismatches.
It works for both python 2.7 and 3+.
"""
def __init__(self, file_path):
super(open_properly, self).__init__()
self.file_path = file_path
def __enter__(self):
self.file_writer = open(self.file_path, 'w', encoding='utf8')
self.file_writer.__enter__()
return self
def write(self, s):
if not isinstance(s, string_types):
raise ValueError("the text to print must be a valid string type")
# make sure it's unicode (important for python 2)
if isinstance(s, str):
if hasattr(s, 'decode'):
s = s.decode('utf-8')
# write unicode to file
self.file_writer.write(s)
def __exit__(self, exc_type, exc_val, exc_tb):
return self.file_writer.__exit__(exc_type, exc_val, exc_tb)