هل يمكنني إعادة توجيه stdout في بيثون إلى نوع من المخزن المؤقت للسلسلة؟
سؤال
أنا أستخدم بيثون ftplib
لكتابة عميل FTP صغير، لكن بعض الوظائف الموجودة في الحزمة لا تُرجع مخرجات السلسلة، بل تطبع إليها stdout
.أريد إعادة التوجيه stdout
إلى كائن سأكون قادرًا على قراءة الإخراج منه.
أنا أعرف stdout
يمكن إعادة توجيهه إلى أي ملف عادي باستخدام:
stdout = open("file", "a")
لكنني أفضل الطريقة التي لا تستخدم محرك الأقراص المحلي.
أنا أبحث عن شيء مثل BufferedReader
في Java والتي يمكن استخدامها لالتفاف المخزن المؤقت في الدفق.
المحلول
from cStringIO import StringIO # Python3 use: from io import StringIO
import sys
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
# blah blah lots of code ...
sys.stdout = old_stdout
# examine mystdout.getvalue()
نصائح أخرى
وهناك contextlib.redirect_stdout () وظيفة في بيثون 3.4:
import io
from contextlib import redirect_stdout
with io.StringIO() as buf, redirect_stdout(buf):
print('redirected')
output = buf.getvalue()
إليك رمز المثال الذي يوضح كيفية تطبيق ذلك على كبار السن الإصدارات بيثون .
فقط للإضافة إلى إجابة نيد أعلاه:يمكنك استخدام هذا لإعادة توجيه الإخراج إلى أي كائن يطبق طريقة الكتابة (str)..
يمكن استخدام هذا لتحقيق تأثير جيد "لالتقاط" إخراج stdout في تطبيق واجهة المستخدم الرسومية.
إليك مثالًا سخيفًا في PyQt:
import sys
from PyQt4 import QtGui
class OutputWindow(QtGui.QPlainTextEdit):
def write(self, txt):
self.appendPlainText(str(txt))
app = QtGui.QApplication(sys.argv)
out = OutputWindow()
sys.stdout=out
out.show()
print "hello world !"
بدءًا من Python 2.6، يمكنك استخدام أي شيء ينفذ TextIOBase
واجهة برمجة التطبيقات من وحدة io كبديل.يمكّنك هذا الحل أيضًا من الاستخدام sys.stdout.buffer.write()
في Python 3 لكتابة سلاسل البايت المشفرة (بالفعل) إلى stdout (انظر stdout في بيثون 3).استخدام StringIO
لن تعمل بعد ذلك، لأنه لا sys.stdout.encoding
ولا sys.stdout.buffer
سيكون متاحا.
الحل باستخدام TextIOWrapper:
import sys
from io import TextIOWrapper, BytesIO
# setup the environment
old_stdout = sys.stdout
sys.stdout = TextIOWrapper(BytesIO(), sys.stdout.encoding)
# do something that writes to stdout or stdout.buffer
# get output
sys.stdout.seek(0) # jump to the start
out = sys.stdout.read() # read output
# restore stdout
sys.stdout.close()
sys.stdout = old_stdout
يعمل هذا الحل مع Python 2 >= 2.6 وPython 3.
يرجى ملاحظة أن جديدنا sys.stdout.write()
يقبل فقط سلاسل Unicode و sys.stdout.buffer.write()
يقبل فقط سلاسل البايت.قد لا يكون هذا هو الحال بالنسبة للتعليمات البرمجية القديمة، ولكن غالبًا ما يكون هذا هو الحال بالنسبة للتعليمات البرمجية التي تم تصميمها للتشغيل على Python 2 و3 بدون تغييرات، والتي غالبًا ما تستخدم مرة أخرى sys.stdout.buffer
.
يمكنك إنشاء تباين طفيف يقبل سلاسل Unicode وbyte لـ write()
:
class StdoutBuffer(TextIOWrapper):
def write(self, string):
try:
return super(StdoutBuffer, self).write(string)
except TypeError:
# redirect encoded byte strings directly to buffer
return super(StdoutBuffer, self).buffer.write(string)
لا يتعين عليك تعيين ترميز المخزن المؤقت على sys.stdout.encoding، ولكن هذا يساعد عند استخدام هذه الطريقة لاختبار/مقارنة مخرجات البرنامج النصي.
تعمل هذه الطريقة على استعادة sys.stdout حتى لو كان هناك استثناء.كما أنه يحصل على أي إخراج قبل الاستثناء.
import io
import sys
real_stdout = sys.stdout
fake_stdout = io.BytesIO() # or perhaps io.StringIO()
try:
sys.stdout = fake_stdout
# do what you have to do to create some output
finally:
sys.stdout = real_stdout
output_string = fake_stdout.getvalue()
fake_stdout.close()
# do what you want with the output_string
تم اختباره في Python 2.7.10 باستخدام io.BytesIO()
تم الاختبار في Python 3.6.4 باستخدام io.StringIO()
تمت إضافة بوب لحالة إذا شعرت أن أي شيء من تجربة التعليمات البرمجية المعدلة/الموسعة قد يصبح مثيرًا للاهتمام بأي حال من الأحوال، وإلا لا تتردد في حذفها
إعلان إعلامي...بعض الملاحظات من التجارب الموسعة أثناء العثور على بعض الآليات القابلة للتطبيق "للحصول على" المخرجات، من إخراج
numexpr.print_versions()
مباشرة إلى<stdout>
(عند الحاجة إلى تنظيف واجهة المستخدم الرسومية وجمع التفاصيل في تقرير تصحيح الأخطاء)
# THIS WORKS AS HELL: as Bob Stein proposed years ago:
# py2 SURPRISEDaBIT:
#
import io
import sys
#
real_stdout = sys.stdout # PUSH <stdout> ( store to REAL_ )
fake_stdout = io.BytesIO() # .DEF FAKE_
try: # FUSED .TRY:
sys.stdout.flush() # .flush() before
sys.stdout = fake_stdout # .SET <stdout> to use FAKE_
# ----------------------------------------- # + do what you gotta do to create some output
print 123456789 # +
import numexpr # +
QuantFX.numexpr.__version__ # + [3] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
QuantFX.numexpr.print_versions() # + [4] via fake_stdout re-assignment, as was bufferred + "late" deferred .get_value()-read into print, to finally reach -> real_stdout
_ = os.system( 'echo os.system() redir-ed' )# + [1] via real_stdout + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
_ = os.write( sys.stderr.fileno(), # + [2] via stderr + "late" deferred .get_value()-read into print, to finally reach -> real_stdout, if not ( _ = )-caught from RET-d "byteswritten" / avoided from being injected int fake_stdout
b'os.write() redir-ed' )# *OTHERWISE, if via fake_stdout, EXC <_io.BytesIO object at 0x02C0BB10> Traceback (most recent call last):
# ----------------------------------------- # ? io.UnsupportedOperation: fileno
#''' ? YET: <_io.BytesIO object at 0x02C0BB10> has a .fileno() method listed
#>>> 'fileno' in dir( sys.stdout ) -> True ? HAS IT ADVERTISED,
#>>> pass; sys.stdout.fileno -> <built-in method fileno of _io.BytesIO object at 0x02C0BB10>
#>>> pass; sys.stdout.fileno()-> Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# io.UnsupportedOperation: fileno
# ? BUT REFUSES TO USE IT
#'''
finally: # == FINALLY:
sys.stdout.flush() # .flush() before ret'd back REAL_
sys.stdout = real_stdout # .SET <stdout> to use POP'd REAL_
sys.stdout.flush() # .flush() after ret'd back REAL_
out_string = fake_stdout.getvalue() # .GET string from FAKE_
fake_stdout.close() # <FD>.close()
# +++++++++++++++++++++++++++++++++++++ # do what you want with the out_string
#
print "\n{0:}\n{1:}{0:}".format( 60 * "/\\",# "LATE" deferred print the out_string at the very end reached -> real_stdout
out_string #
)
'''
PASS'd:::::
...
os.system() redir-ed
os.write() redir-ed
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
123456789
'2.5'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version: 2.5
NumPy version: 1.10.4
Python version: 2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
AMD/Intel CPU? True
VML available? True
VML/MKL version: Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
Number of threads used by default: 4 (out of 4 detected cores)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
>>>
EXC'd :::::
...
os.system() redir-ed
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
123456789
'2.5'
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Numexpr version: 2.5
NumPy version: 1.10.4
Python version: 2.7.13 |Anaconda 4.0.0 (32-bit)| (default, May 11 2017, 14:07:41) [MSC v.1500 32 bit (Intel)]
AMD/Intel CPU? True
VML available? True
VML/MKL version: Intel(R) Math Kernel Library Version 11.3.1 Product Build 20151021 for 32-bit applications
Number of threads used by default: 4 (out of 4 detected cores)
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
Traceback (most recent call last):
File "<stdin>", line 9, in <module>
io.UnsupportedOperation: fileno
'''
في Python3.6، فقد ولت وحدات StringIO
وcStringIO
، يجب عليك استخدام io.StringIO
instead.So يجب عليك القيام بذلك مثل الجواب الأول:
import sys
from io import StringIO
old_stdout = sys.stdout
old_stderr = sys.stderr
my_stdout = sys.stdout = StringIO()
my_stderr = sys.stderr = StringIO()
# blah blah lots of code ...
sys.stdout = self.old_stdout
sys.stderr = self.old_stderr
// if you want to see the value of redirect output, be sure the std output is turn back
print(my_stdout.getvalue())
print(my_stderr.getvalue())
my_stdout.close()
my_stderr.close()
استخدم pipe()
والكتابة إلى واصف الملف المناسب.
https://docs.python.org/library/os أتش تي أم أل # ملف واصف-عمليات
وقال مدير سياق python3:
import sys
from io import StringIO
class RedirectedStdout:
def __init__(self):
self._stdout = None
self._string_io = None
def __enter__(self):
self._stdout = sys.stdout
sys.stdout = self._string_io = StringIO()
return self
def __exit__(self, type, value, traceback):
sys.stdout = self._stdout
def __str__(self):
return self._string_io.getvalue()
واستخدام مثل هذا:
>>> with RedirectedStdout() as out:
>>> print('asdf')
>>> s = str(out)
>>> print('bsdf')
>>> print(s, out)
'asdf\n' 'asdf\nbsdf\n'