เปลี่ยนเส้นทาง stdout เป็น "nothing" ใน python


130

ฉันมีโปรเจ็กต์ขนาดใหญ่ที่ประกอบด้วยโมดูลจำนวนมากเพียงพอโดยแต่ละชิ้นจะพิมพ์บางอย่างไปยังเอาต์พุตมาตรฐาน ตอนนี้เนื่องจากโครงการมีขนาดใหญ่ขึ้นเรื่อย ๆ ของprintงบการพิมพ์มากในการออกมาตรฐานซึ่งได้ทำโปรแกรมช้ามาก

ตอนนี้ฉันต้องการตัดสินใจที่รันไทม์ว่าจะพิมพ์อะไรลงใน stdout หรือไม่ ฉันไม่สามารถทำการเปลี่ยนแปลงในโมดูลได้เนื่องจากมีโมดูลมากมาย (ฉันรู้ว่าฉันสามารถเปลี่ยนเส้นทาง stdout ไปยังไฟล์ได้ แต่มันก็ช้ามาก)

ดังนั้นคำถามของฉันคือฉันจะเปลี่ยนเส้นทาง stdout ไปเป็น nothing ได้อย่างไรเช่นฉันจะทำให้printคำสั่งไม่ทำอะไรได้อย่างไร

# I want to do something like this.
sys.stdout = None         # this obviously will give an error as Nonetype object does not have any write method.

ตอนนี้ความคิดเดียวที่ฉันมีคือสร้างคลาสที่มีวิธีการเขียน (ซึ่งไม่ทำอะไรเลย) และเปลี่ยนเส้นทาง stdout ไปยังอินสแตนซ์ของคลาสนี้

class DontPrint(object):
    def write(*args): pass

dp = DontPrint()
sys.stdout = dp

มีกลไก inbuilt ใน python สำหรับสิ่งนี้หรือไม่? หรือมีอะไรที่ดีกว่านี้?



1
ฉันเพิ่งค้นพบสิ่งที่แปลกประหลาด การเปลี่ยน sys.stdout เป็นไม่มีใช้งานได้จริงในโปรแกรมของฉันแม้ว่าฉันจะไม่แน่ใจว่าทำไม ผมมีจริงๆปัญหาแปลกที่มีการเข้ารหัสการเปลี่ยนเส้นทางไป os.devnull ดังนั้นผมจึงพยายามเพียงแค่ใช้ไม่มีและการทำงาน อาจมีบางอย่างเกี่ยวข้องกับฉันในการทดสอบหน่วย Django แต่ฉันไม่แน่ใจ
Teekin

คำตอบ:


233

ข้ามแพลตฟอร์ม:

import os
import sys
f = open(os.devnull, 'w')
sys.stdout = f

บน Windows:

f = open('nul', 'w')
sys.stdout = f

บน Linux:

f = open('/dev/null', 'w')
sys.stdout = f

แล้วคุณจะยกเลิกได้อย่างไร? มีวิธีเปิดใช้คำสั่งพิมพ์อีกครั้งหลังจากทำเสร็จแล้วหรือไม่?
jj172

4
ได้เลยเพียงบันทึกการอ้างอิงไปยังต้นฉบับsys.stdoutและตั้งค่ากลับมา ตัวอย่างเช่นold_stdout = sys.stdoutก่อนรหัสอื่น ๆ จากนั้นsys.stdout = old_stdoutเมื่อคุณทำเสร็จแล้ว
Andrew Clark

1
หมายเหตุ: มันไม่ได้เปลี่ยนเส้นทางไปที่ระดับคือไฟล์อธิบายก็อาจล้มเหลวที่จะเปลี่ยนเส้นทางการส่งออกของกระบวนการที่เด็กภายนอกออกจากส่วนขยาย C ที่พิมพ์ไปที่ C stdout os.write(1, b'abc\n')โดยตรง คุณต้องos.dup2()เปลี่ยนเส้นทางไปที่ระดับตัวอธิบายไฟล์โปรดดูที่stdout_redirected()
jfs

4
โปรดทราบว่ามีsys.__stdout__ไว้สำหรับการยกเลิกการเปลี่ยนเส้นทาง คุณก็ทำsys.stdout = sys.__stdout__และไม่จำเป็นต้องจัดเก็บข้อมูลสำรอง
Konstantin Sekeresh

30

วิธีที่ดีในการทำเช่นนี้คือการสร้างตัวประมวลผลบริบทขนาดเล็กที่คุณห่อผลงานพิมพ์ของคุณจากนั้นคุณก็ใช้ในการแสดงสถานะwithเพื่อปิดเสียงเอาต์พุตทั้งหมด

Python 2:

import os
import sys
from contextlib import contextmanager

@contextmanager
def silence_stdout():
    old_target = sys.stdout
    try:
        with open(os.devnull, "w") as new_target:
            sys.stdout = new_target
            yield new_target
    finally:
        sys.stdout = old_target

with silence_stdout():
    print("will not print")

print("this will print")

Python 3.4+:

Python 3.4 มีตัวประมวลผลบริบทเช่นนี้ในตัวดังนั้นคุณสามารถใช้ contextlib ดังนี้:

import contextlib

with contextlib.redirect_stdout(None):
    print("will not print")

print("this will print")

การรันโค้ดนี้จะพิมพ์เฉพาะบรรทัดที่สองของเอาต์พุตไม่ใช่บรรทัดแรก:

$ python test.py
this will print

สิ่งนี้ใช้งานได้ข้ามแพลตฟอร์ม (Windows + Linux + Mac OSX) และสะอาดกว่าคำตอบอื่น ๆ imho


@celticminstrel คุณพูดถูกต้องขอบคุณที่แก้ไขฉัน ฉันได้อัปเดตรหัสเพื่อรีเซ็ต sys.stdout ในภายหลัง
Emil Stenström

สิ่งนี้ขาดการตรวจสอบข้อผิดพลาดบางอย่าง แต่เป็นความคิดที่ดี
นักฟิสิกส์บ้า

เพิ่มโค้ดเพื่อแสดงวิธีการทำงานใน Python 3 ด้วย
Emil Stenström

15

หากคุณอยู่ใน python 3.4 หรือสูงกว่ามีวิธีง่ายๆและปลอดภัยโดยใช้ไลบรารีมาตรฐาน:

import contextlib

with contextlib.redirect_stdout(None):
  print("This won't print!")

3
with contextlib.redirect_stdout(None)ได้ผลเช่นกัน
Chris Cogdon

@ChrisCogdon คำใบ้ที่ดีมากฉันแก้ไขคำตอบตามนั้น
iFreilicht

1
ไม่ทำงานถ้าคุณใช้ไลบรารี pprint AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy

@Speeeddy คุณได้รับข้อผิดพลาดนี้กับ python 2 หรือไม่? คุณสามารถสร้างคลาสด้วยwriteวิธีหลอกที่ไม่ทำอะไรเลย ดูคำตอบของฉันด้านล่าง
dizcza

8

(อย่างน้อยในระบบของฉัน) ดูเหมือนว่าการเขียนไปยัง os.devnull จะเร็วกว่าการเขียนลงในคลาส DontPrint ประมาณ 5 เท่ากล่าวคือ

#!/usr/bin/python
import os
import sys
import datetime

ITER = 10000000
def printlots(out, it, st="abcdefghijklmnopqrstuvwxyz1234567890"):
   temp = sys.stdout
   sys.stdout = out
   i = 0
   start_t = datetime.datetime.now()
   while i < it:
      print st
      i = i+1
   end_t = datetime.datetime.now()
   sys.stdout = temp
   print out, "\n   took", end_t - start_t, "for", it, "iterations"

class devnull():
   def write(*args):
      pass


printlots(open(os.devnull, 'wb'), ITER)
printlots(devnull(), ITER)

ให้ผลลัพธ์ต่อไปนี้:

<open file '/dev/null', mode 'wb' at 0x7f2b747044b0> 
   took 0:00:02.074853 for 10000000 iterations
<__main__.devnull instance at 0x7f2b746bae18> 
   took 0:00:09.933056 for 10000000 iterations

4
สำหรับการบันทึกครั้งต่อไปที่คุณต้องการเวลาบางสิ่งเพียงใช้timeit
Antoine Pelisse

6

หากคุณอยู่ในสภาพแวดล้อม Unix (รวม Linux) คุณสามารถเปลี่ยนเส้นทางเอาต์พุตไปที่/dev/null:

python myprogram.py > /dev/null

และสำหรับ Windows:

python myprogram.py > nul

2
ดีกว่าที่จะมีโซลูชันที่ใช้ได้กับทุกแพลตฟอร์ม
Pushpak Dagade

2
@Guanidene บางที แต่ถ้าเขาเป็นคนเดียวที่ใช้โปรแกรมของเขาเขาก็สามารถรับประกันสิ่งแวดล้อมได้มาก
Nick Radford

nul = myfunc() ?
Hicsy

1

ชั้นเรียนของคุณจะทำงานได้ดี (ยกเว้นwrite()ชื่อเมธอด - ต้องเรียกเป็นwrite()ตัวพิมพ์เล็ก) ตรวจสอบให้แน่ใจว่าคุณได้บันทึกสำเนาsys.stdoutในตัวแปรอื่น

หากคุณใช้ * NIX คุณสามารถทำได้sys.stdout = open('/dev/null')แต่จะพกพาได้น้อยกว่าการเล่นคลาสของคุณเอง


1

แล้วสิ่งนี้ล่ะ:

from contextlib import ExitStack, redirect_stdout
import os

with ExitStack() as stack:
    if should_hide_output():
        null_stream = open(os.devnull, "w")
        stack.enter_context(null_stream)
        stack.enter_context(redirect_stdout(null_stream))
    noisy_function()

สิ่งนี้ใช้คุณสมบัติในโมดูลบริบทลิบเพื่อซ่อนเอาต์พุตของคำสั่งใด ๆ ที่คุณพยายามรันขึ้นอยู่กับผลลัพธ์ของshould_hide_output()และเรียกคืนพฤติกรรมเอาต์พุตหลังจากที่ฟังก์ชันนั้นทำงานเสร็จแล้ว

หากคุณต้องการที่จะซ่อนออกข้อผิดพลาดมาตรฐานแล้วนำเข้าredirect_stderrจากและเพิ่มบรรทัดว่าcontextlibstack.enter_context(redirect_stderr(null_stream))

ข้อเสียหลักคือสิ่งนี้ใช้ได้เฉพาะใน Python 3.4 และเวอร์ชันที่ใหม่กว่า




0

ทำไมคุณไม่ลองทำดู?

sys.stdout.close()
sys.stderr.close()

เพราะ A) มันอาจจะใช้ไม่ได้ด้วยซ้ำและ B) ถ้าเป็นเช่นนั้นคุณจะได้รับข้อผิดพลาดแทนที่จะระงับผลลัพธ์
Mad Physicist

0
sys.stdout = None

เป็นprint()กรณีที่ใช้ได้ แต่อาจทำให้เกิดข้อผิดพลาดได้หากคุณเรียกใช้วิธีใด ๆ ของ sys.stdout เช่นsys.stdout.write().

มีหมายเหตุในเอกสาร :

ภายใต้เงื่อนไขบางประการ stdin, stdout และ stderr ตลอดจนค่าดั้งเดิมstdin , stdoutและstderrสามารถเป็น None ได้ โดยปกติจะเป็นกรณีสำหรับแอป Windows GUI ที่ไม่ได้เชื่อมต่อกับคอนโซลและแอป Python ที่เริ่มต้นด้วย pythonw


"ตกลง" และ "จะไม่แสดงข้อผิดพลาดในขณะที่คุณพยายามพิมพ์" เป็นสิ่งที่แตกต่างกันมากในกรณีนี้
Mad Physicist

1
คุณถูก. คุณไม่สามารถเรียกใช้วิธีการใด ๆ ได้sys.stdout = Nullในบางกรณีอาจเป็นอันตรายได้
Alexander Chzhen

ในกรณีนี้เป็นที่ทราบกันดีว่าเป็นอันตราย OP ระบุข้อผิดพลาดที่ได้รับจากการทำโดยเฉพาะ
Mad Physicist

0

ส่วนเสริมสำหรับคำตอบของ iFreilicht - ใช้ได้กับทั้ง python 2 และ 3

import sys

class NonWritable:
    def write(self, *args, **kwargs):
        pass

class StdoutIgnore:
    def __enter__(self):
        self.stdout_saved = sys.stdout
        sys.stdout = NonWritable()
        return self

    def __exit__(self, *args):
        sys.stdout = self.stdout_saved

with StdoutIgnore():
    print("This won't print!")
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.