Chuyển hướng stdout sang không có gì


128

Tôi có một dự án lớn bao gồm đủ số lượng lớn các mô-đun, mỗi mô-đun in một cái gì đó đến đầu ra tiêu chuẩn. Bây giờ khi dự án đã tăng kích thước, không có lớn. của printbáo cáo in nhiều vào ra std mà đã có những chương trình đáng kể chậm hơn.

Vì vậy, bây giờ tôi muốn quyết định trong thời gian chạy có hay không in bất cứ thứ gì lên thiết bị xuất chuẩn. Tôi không thể thay đổi các mô-đun vì có rất nhiều trong số chúng. (Tôi biết rằng tôi có thể chuyển hướng thiết bị xuất chuẩn đến một tệp nhưng ngay cả điều này cũng khá chậm.)

Vì vậy, câu hỏi của tôi là làm thế nào để tôi chuyển hướng thiết bị xuất chuẩn thành không có nghĩa là làm thế nào để tôi printtuyên bố không làm gì?

# 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.

Hiện tại ý tưởng duy nhất tôi có là tạo một lớp có phương thức ghi (không làm gì) và chuyển hướng thiết bị xuất chuẩn sang một thể hiện của lớp này.

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

dp = DontPrint()
sys.stdout = dp

Có một cơ chế sẵn có trong python cho điều này? Hoặc có một cái gì đó tốt hơn thế này?



1
Tôi vừa mới phát hiện ra một điều kỳ dị. Biến sys.stdout thành Không thực sự hoạt động trong chương trình của tôi, mặc dù tôi không chắc tại sao. Tôi đã gặp vấn đề thực sự kỳ lạ với việc mã hóa chuyển hướng sang os.devnull vì vậy tôi đã thử chỉ sử dụng Không, và nó hoạt động. Có thể có điều gì đó để làm với tôi khi làm bài kiểm tra đơn vị Django, nhưng tôi không chắc.
Teekin

Câu trả lời:


233

Đa nền tảng:

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

Trên Windows:

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

Trên Linux:

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

Làm thế nào để bạn sau đó hủy bỏ nó? Có cách nào để bật lại báo cáo in sau khi bạn hoàn thành không?
jj172

4
Chắc chắn, chỉ cần lưu một tham chiếu đến bản gốc sys.stdoutvà đặt lại sau. Ví dụ old_stdout = sys.stdouttrước bất kỳ mã nào khác, sau đó sys.stdout = old_stdoutkhi bạn hoàn thành.
Andrew Clark

1
lưu ý: nó không chuyển hướng ở mức mô tả tệp, nghĩa là, nó có thể không chuyển hướng đầu ra của các tiến trình con bên ngoài, đầu ra từ các phần mở rộng C in ra trực tiếp C stdout , os.write(1, b'abc\n'). Bạn cần os.dup2(), để chuyển hướng ở cấp mô tả tệp, xemstdout_redirected()
jfs

4
Lưu ý là sys.__stdout__để hủy bỏ chuyển hướng. Vì vậy, bạn chỉ cần làm sys.stdout = sys.__stdout__và không cần lưu trữ bản sao lưu.
Konstantin Sekeresh

28

Một cách hay để làm điều này là tạo một bộ xử lý bối cảnh nhỏ mà bạn bọc các bản in của mình. Sau đó, bạn chỉ cần sử dụng là ở chế độ withđể tắt tất cả đầu ra.

Con trăn 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 có bộ xử lý bối cảnh như tích hợp sẵn này, vì vậy bạn chỉ cần sử dụng bối cảnh như thế này:

import contextlib

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

print("this will print")

Chạy mã này chỉ in dòng đầu ra thứ hai, không in dòng đầu tiên:

$ python test.py
this will print

Điều này hoạt động đa nền tảng (Windows + Linux + Mac OSX) và sạch hơn các câu trả lời khác.


@celticminstrel bạn hoàn toàn đúng, cảm ơn vì đã sửa lỗi cho tôi. Tôi đã cập nhật mã để thiết lập lại sys.stdout sau đó.
Emil Stenström

Điều này thiếu một số kiểm tra lỗi, nhưng ý tưởng tuyệt vời
Nhà vật lý điên

Đã thêm mã để hiển thị cách thức hoạt động trong Python 3.
Emil Stenström

15

Nếu bạn ở python 3,4 trở lên, có một giải pháp đơn giản và an toàn bằng thư viện chuẩn:

import contextlib

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

3
with contextlib.redirect_stdout(None)cũng hoạt động tốt
Chris Cogdon

@ChrisCogdon Gợi ý rất hay, tôi đã chỉnh sửa câu trả lời cho phù hợp.
iFreilicht

1
Không hoạt động nếu bạn đang sử dụng thư viện pprint. AttributeError: 'NoneType' object has no attribute 'write'
Speeeddy

@Speeeddy bạn có gặp lỗi này với python 2 không? Bạn có thể tạo một lớp với một writephương thức giả mà không làm gì cả. Xem câu trả lời của tôi dưới đây.
dizcza

8

(ít nhất là trên hệ thống của tôi) có vẻ như việc viết cho os.devnull nhanh hơn khoảng 5 lần so với việc viết cho lớp DontPrint, tức là

#!/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)

đã cho đầu ra sau:

<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
Chỉ cần cho các hồ sơ, thời gian tiếp theo bạn muốn thời gian một cái gì đó, chỉ cần sử dụng timeit
Antoine áo choàng của trẻ con

6

Nếu bạn đang ở trong môi trường Unix (bao gồm Linux), bạn có thể chuyển hướng đầu ra sang /dev/null:

python myprogram.py > /dev/null

Và đối với Windows:

python myprogram.py > nul

2
Tốt hơn là có một giải pháp sẽ hoạt động trên tất cả các nền tảng.
Pushpak Dagade

2
@Guanidene, có lẽ, nhưng nếu anh ấy là người duy nhất sử dụng chương trình của mình, anh ấy có thể đảm bảo môi trường.
Nick Radford

nul = myfunc() ?
Hicsy

2

Còn cái này thì sao:

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()

Này sử dụng các tính năng trong contextlib module để giấu đầu ra của bất cứ lệnh bạn đang cố gắng để chạy, tùy thuộc vào kết quả của should_hide_output(), và sau đó phục hồi hành vi đầu ra sau khi chức năng đó là thực hiện hoạt động.

Nếu bạn muốn ẩn đầu ra lỗi tiêu chuẩn, sau đó nhập redirect_stderrtừ contextlibvà thêm một dòng nói stack.enter_context(redirect_stderr(null_stream)).

Nhược điểm chính là nó chỉ hoạt động trong Python 3.4 và các phiên bản mới hơn.



1

Lớp của bạn sẽ hoạt động tốt (ngoại trừ write()tên phương thức - nó cần được gọi write(), chữ thường). Chỉ cần chắc chắn rằng bạn lưu một bản sao của sys.stdoutmột biến khác.

Nếu bạn đang sử dụng * NIX, bạn có thể làm được sys.stdout = open('/dev/null'), nhưng điều này ít mang tính di động hơn so với việc lăn lớp của chính bạn.


1

Bạn chỉ có thể chế giễu nó.

import mock

sys.stdout = mock.MagicMock()

0

Tại sao bạn không thử điều này?

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

Bởi vì A) nó thậm chí có thể không hoạt động và B) nếu có, bạn sẽ gặp lỗi thay vì chỉ triệt tiêu đầu ra.
Nhà vật lý điên

0
sys.stdout = None

Nó là OK cho print()trường hợp. Nhưng nó có thể gây ra lỗi nếu bạn gọi bất kỳ phương thức nào của sys.stdout, vd sys.stdout.write().

Có một lưu ý trong tài liệu :

Trong một số điều kiện stdin, stdout và stderr cũng như các giá trị ban đầu stdin , stdoutstderr có thể là Không có. Nó thường là trường hợp cho các ứng dụng GUI của Windows không được kết nối với bảng điều khiển và các ứng dụng Python bắt đầu bằng pythonw.


"OK" và "Sẽ không gây ra lỗi khi bạn cố in" là những điều rất khác nhau trong trường hợp này.
Nhà vật lý điên

1
Bạn đúng. Bạn không thể gọi bất kỳ phương pháp nào sys.stdout = Nulltrong một số trường hợp có thể nguy hiểm.
Alexander Chzhen 23/07/18

Trong trường hợp chính xác này, nó được biết là nguy hiểm. OP đặc biệt giải quyết lỗi mà anh ta nhận được từ việc đó.
Nhà vật lý điên

0

Bổ sung cho câu trả lời của iFreilicht - nó hoạt động cho cả 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!")
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.