RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1608292
Accepted
Alexey Trukhanov
Alexey Trukhanov
Asked:2025-03-07 15:55:02 +0000 UTC2025-03-07 15:55:02 +0000 UTC 2025-03-07 15:55:02 +0000 UTC

如何禁用函数内部的打印功能?

  • 772

鉴于

有许多函数可以执行一些计算。在某些情况下(为了调试或监控过程),定期打印计算结果是很方便的。像这样:

def some_calculations(b, a):
    print('Начали вычислять...')
    # чего-то вычисляем
    s = b + a
    print(f'Закончили вычислять. Результаты: {s}')

    print('Опять начали вычислять...')
    # чего-то вычисляем
    p = b * a
    print(f'Закончили вычислять. Результаты: {p}')
    
    # и тут таких или подобных итераций несколько
    
    return s, p

但在某些情况下,为了不浪费时间在长时间的输出操作上,我需要禁用所有这些打印。首先,我想定义某种可以传递给这些函数的布尔变量,并在每次打印附近描述根据这个布尔变量是否输出/不输出的条件。但后来我想一定有办法可以关闭所有的打印。我没有依靠自己的知识,而是去寻找并找到了它。

# выключили
sys.stdout = open(os.devnull, 'w')
# включили
sys.stdout = sys.__stdout__

接下来,我决定测量执行时间

  1. 只是功能
  2. 输出关闭
  3. 打印被包装在取决于布尔标志变量的条件中的函数。

以下是带有标志的版本:

def some_calculations(b, a, flag = False):
    if flag:
        print('Начали вычислять...')
    # чего-то вычисляем
    s = b + a
    if flag:
        print(f'Закончили вычислять. Результаты: {s}')

    if flag:
        print('Опять начали вычислять...')
    # чего-то вычисляем
    p = b * a
    if flag:
        print(f'Закончили вычислять. Результаты: {p}')

    # и тут таких или подобных итераций несколько

    return s, p

测量结果表明,关闭输出的选项可使打印函数的执行时间缩短5 倍,而带有标志的选项可使执行时间缩短 65 倍。造成这种差异的原因很明显——开头指出的方法不会禁用打印,它仍然执行功能print,仅输出到devnull,也就是说,它只是抑制输出到控制台。并且标志绕过打印功能。

问题

是否有可能禁用某个函数(例如打印),而无需将每个函数调用包装在条件中,从而使禁用实际上绕过该函数,从而提供运行时好处,而不是简单地抑制输出?

python
  • 6 6 个回答
  • 193 Views

6 个回答

  • Voted
  1. Best Answer
    CrazyElf
    2025-03-07T17:11:05Z2025-03-07T17:11:05Z

    这是 Python,您可以在这里重新定义几乎任何东西。那些。您甚至可以覆盖该函数,print使其不执行任何操作:

    save_print = print
    print = lambda x: None
    some_calculations(1, 2)
    print = save_print
    

    但总的来说,最好学习日志记录,它正是为这样的使用场景而发明的——有时你需要输出信息,有时则不需要。有时,需要不同程度的细节。

    附言关于正确记录:

    import logging
    
    logger = logging.getLogger(__name__)
    logging.basicConfig(filename='example.log', encoding='utf-8', level=logging.INFO) # logging.DEBUG
    
    def some_calculations(b, a):
        logger.debug('Начали вычислять...')
        # чего-то вычисляем
        s = b + a
        logger.debug('Закончили вычислять. Результаты: %s', s)
    
        logger.debug('Опять начали вычислять...')
        # чего-то вычисляем
        p = b * a
        logger.debug('Закончили вычислять. Результаты: %s', p)
        
        # и тут таких или подобных итераций несколько
        
        return s, p
    

    使用此日志记录选项,如文档中所写:

    消息参数的格式化被推迟,直到无法避免为止。

    也就是说,例如,如果您设置日志记录级别,使得记录器调用不应将任何内容写入日志,则参数将不会被替换到模板中。尽管这仍然比非日志版本慢,但它不需要进行棘手的代码修改就可以获得带日志和不带日志的选项。在记录器配置中指定所需的日志记录级别就足够了。

    函数变体 营业时间 相对于基础的放缓
    输出已被注释掉。 200纳秒 根据
    调试级别 160 毫秒 800次
    信息级别 2.3 毫秒 11次
    INFO 级别 + 模板 1.8 毫秒 9次
    • 7
  2. Dmitry
    2025-03-07T16:13:20Z2025-03-07T16:13:20Z

    我将扩展评论中的想法:编写一个禁用标准输出的类。通过构造调用函数with

    import os, sys
    
    
    class WrapperNoPrints:
        def __enter__(self):
            self._original_stdout = sys.stdout
            sys.stdout = None
        def __exit__(self, exc_type, exc_val, exc_tb):
            sys.stdout = self._original_stdout
    

    和挑战

    with WrapperNoPrints():
        s, p = some_calculations(1, 2)
    

    嗯,这是控制台的结果。

    >>> with WrapperNoPrints():
    ...   s, p = some_calculations(1, 2)
    ... 
    >>> s
    3
    >>> p
    2
    >>>
    >>>
    >>> s, p = some_calculations(1, 2)
    Начали вычислять...
    Закончили вычислять. Результаты: 3
    Опять начали вычислять...
    Закончили вычислять. Результаты: 2
    
    • 6
  3. Maksim Alekseev
    2025-03-07T18:06:00Z2025-03-07T18:06:00Z

    最好使用专为此类操作设计的包。logging

    例子:

    • 必要的导入
    import os
    import sys
    import logging
    from dotenv import load_dotenv
    
    • 我们从环境中获取日志记录级别:
    load_dotenv()
    
    LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO")
    
    • 创建您自己的记录器实例
    stream_handler = logging.StreamHandler(stream=sys.stdout) # Обработчик логов в stdout
    logger = logging.getLogger(__name__) # Наш логер
    logger.setLevel(LOG_LEVEL) # Устанавливаем уровень
    logger.addHandler(stream_handler) # Устанавливаем обработчик
    
    logger.info(f"Logging level - {LOG_LEVEL}")
    
    • 让我们运行代码:
    def some_calculations(b, a):
        logger.debug('Начали вычислять...')
        # чего-то вычисляем
        s = b + a
        logger.debug(f'Закончили вычислять. Результаты: {s}')
    
        logger.debug('Опять начали вычислять...')
        # чего-то вычисляем
        p = b * a
        logger.debug(f'Закончили вычислять. Результаты: {p}')
    
        return s, p
    
    some_calculations(1, 3)
    
    • 输出(级别DEBUG):
    Logging level - DEBUG
    Начали вычислять...
    Закончили вычислять. Результаты: 4
    Опять начали вычислять...
    Закончили вычислять. Результаты: 3
    
    • 输出(级别INFO):
    Logging level - INFO
    
    • 6
  4. Stanislav Volodarskiy
    2025-03-07T18:55:12Z2025-03-07T18:55:12Z

    此代码对我4.1c有效:

    LOG = True
    
    
    def log(message):
        if LOG:
            print(message)
    
    
    def calc_sum(a, b):
        c = a + b
        log(f'calc_sum({a}, {b}) == {c}')
        return c
    
    
    for _ in range(10_000_000):
        c = calc_sum(40, 2)
    
    $ time python log.py | tail -1
    calc_sum(40, 2) == 42
    
    real  0m4.089s
    user  0m4.135s
    sys   0m0.138s
    

    如果您“关闭”日志记录(LOG = False),则2.2 秒:

    $ time python log.py | tail -1
    
    real  0m2.197s
    user  0m2.197s
    sys   0m0.001s
    

    如果你完全注释掉调用log(# log(f'calc_sum({a}, {b}) == {c}'),那么0.6s:

    $ time python log.py | tail -1
    
    real  0m0.575s
    user  0m0.571s
    sys   0m0.004s
    

    我们将差值4.1 - 0.6 = 3.5作为100%。然后“禁用”日志(4.1 - 2.2 = 1.9)返回54%。如果我们完全取消挑战,我们将再获得46% 。

    因为挑战log(f'calc_sum({a}, {b}) == {c}')是分两个阶段计算的。

    第一步是计算f'...'。虽然它被称为字符串,但它并不是文字,而是一个成熟的函数调用。通过“禁用”日志记录,我们并没有删除其计算,但它仍然浪费时间。

    第二阶段是印刷本身。 “禁用”可以阻止它,这里一切都很好。

    结果,我们的“开关”只起到了一半的作用。如果我们想保留格式字符串的语法,这几乎是不可避免的。我们希望它能够方便。因此我们需要一个选项if LOG: print(...):

    LOG = True
    
    
    def calc_sum(a, b):
        c = a + b
        if LOG:
            print(f'calc_sum({a}, {b}) == {c}')
        return c
    
    
    for _ in range(10_000_000):
        c = calc_sum(40, 2)
    

    3.8c启用日志。

    $ time python log.py | tail -1
    calc_sum(40, 2) == 42
    
    real  0m3.811s
    user  0m3.863s
    sys   0m0.124s
    

    略高于0.6c,且对数为“关闭”( LOG = False):

    $ time python log.py | tail -1
    
    real  0m0.623s
    user  0m0.619s
    sys   0m0.004s
    

    略低于0.6c ,带有注释代码。

    $ time py temp.py | tail -1
    
    real  0m0.562s
    user  0m0.554s
    sys   0m0.008s
    

    在此选项中,“禁用”可节省98%的时间。尽管并不美观,但这已经是一个可行的选择。因此,使用if与选项一样快的折衷方案if:

    def calc_sum(a, b):
        c = a + b
        if LOG: print(f'calc_sum({a}, {b}) == {c}')
        return c
    
    def calc_sum(a, b):
        c = a + b
        LOG and print(f'calc_sum({a}, {b}) == {c}')
        return c
    
    • 5
  5. Dezmonder
    2025-03-07T16:15:56Z2025-03-07T16:15:56Z

    只需创建自己的带有条件的打印函数即可。例如:

    is_p = False
    
    def my_print(val):
        if is_p:
            print(val)
    
    my_print(5)
    is_p = True
    my_print("test")
    

    该标志is_p既可以位于函数本身中,也可以位于类中(与函数一起)。通过改变标志,您可以改变行为。

    更新。类似的方法,但不使用全局变量

    def my_print(val, flg=True):
        if flg:
            print(val)
    
    def my_test_func():
        with_print = False
    
        my_print('без передачи флага')
    
        my_print('c передачей флага', with_print)
    
    my_test_func()
    

    一般来说,更正确的解决方案可能是创建自己的记录器类,根据其设置可以更方便地配置打印/输出到文件(例如,添加调用时间)

    • 1
  6. Intelligent Shade of Blue
    2025-03-08T03:35:14Z2025-03-08T03:35:14Z

    这是Dmitry和CrazyElf的答案的组合:

    import builtins
    
    class no_print:
        def __enter__(self):
            self.print = builtins.print
            builtins.print = lambda *args, **kwargs: None
            return self
        
        def __exit__(self, exc_type, exc_val, exc_tb):
            builtins.print = self.print
            return False  # Don't suppress exceptions
    

    使用方式如下:

    def some_calculations(b, a):
        print('Начали вычислять...')
        # чего-то вычисляем
        s = b + a
        print(f'Закончили вычислять. Результаты: {s}')
    
        print('Опять начали вычислять...')
        # чего-то вычисляем
        p = b * a
        print(f'Закончили вычислять. Результаты: {p}')
        
        # и тут таких или подобных итераций несколько
        return s, p
    
    some_calculations(1, 2)
    
    with no_print():
        some_calculations(3, 4)
    

    实例:https://www.online-python.com/G17kaOdlZw

    • 1

相关问题

  • 是否可以以某种方式自定义 QTabWidget?

  • telebot.anihelper.ApiException 错误

  • Python。检查一个数字是否是 3 的幂。输出 无

  • 解析多个响应

  • 交换两个数组的元素,以便它们的新内容也反转

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5