1

there is a thing I want to eliminate, it is when the function print to output, I know about overriding sys.stdout.write which print() function uses. but I think this isn't an ideal fix for this. I'm looking for the ideal pythonic way for doing this?

from timeit import Timer
from functools import partial

def timing_decorator(function):
    def wrapper(*args, **kwargs):
        # print(function)
        callable_f = partial(function, *args, **kwargs)
        # print(dir(Timer))
        total = Timer(callable_f).timeit(1)
        print(total)
        return total
    return wrapper

# example function
@timing_decorator
def hello(arg):
    print(arg)
    return

hello('hi')

This outputs:

hi
0.00036

My expected output would be:

0.00036
  • 2
    your function has a line that shows `print(arg)`, if you don't want it to print the `arg` then remove that line – gold_cy Feb 17 '20 at 18:19
  • @gold_cy i know about that, i want to suppress that without removing the print() function – denthusiast Feb 17 '20 at 18:21
  • Does this answer your question? [Silence the stdout of a function in Python without trashing sys.stdout and restoring each function call](https://stackoverflow.com/questions/2828953/silence-the-stdout-of-a-function-in-python-without-trashing-sys-stdout-and-resto). Putting [vaultah's solution](https://stackoverflow.com/a/28321717/4518341) in your decorator worked for me. – wjandrea Feb 17 '20 at 18:31
  • 2
    Please don't vandalize your own posts. When you post here, you give SO the right to distribute the content under CC-by SA 4.0. Any vandalism will be reverted. If you want to delete your question there is a 'delete' link just below the question text. – greg-449 Feb 17 '20 at 18:37
  • @greg-449 Sorry boss but StackOverflow says that I cannot delete unfortunately – denthusiast Feb 17 '20 at 18:45

2 Answers2

1

We can use patch from the unittest module of the standard library to override the print functionality only when calling a function that uses this wrapper by using a context-manager. However, with that being said, the approach that @kindall takes is preferred as it is more Pythonic given this answer is using something inherently meant for testing purposes.

from unittest.mock import patch

def timing_decorator(function):
    def wrapper(*args, **kwargs):
        # print(function)
        callable_f = partial(function, *args, **kwargs)
        with patch('__main__.print'):
            total = Timer(callable_f).timeit(1)
        print(f"{function.__name__}: {total:.5f}ms")
        return total
    return wrapper
gold_cy
  • 13,648
  • 3
  • 23
  • 45
0

You can create a file-like object that has a write() method that does nothing, and have your decorator substitute that for sys.stdout (and restore the original stdout after calling the function).

import sys

class NoOutput:
    def write(text):
        pass

no_output = NoOutput()

def timing_decorator(function):
    def wrapper(*args, **kwargs):
        callable_f = partial(function, *args, **kwargs)
        old_stdout, sys.stdout = sys.stdout, no_output
        try:
            total = Timer(callable_f).timeit(1)
        finally:
            sys.stdout = old_stdout
        print(f"{function.__name__}: {total:.5f}ms")
        return total
    return wrapper

Note use of try/finally to make sure stdout gets restored even if an exception is raised in the function.

kindall
  • 178,883
  • 35
  • 278
  • 309