【问题标题】:Python warnings come after thing trying to warn user aboutPython警告出现在试图警告用户的事情之后
【发布时间】:2017-04-19 20:37:46
【问题描述】:

我在目标代码中使用警告来提醒用户发生了某些事情,但不会停止代码。下面是一个基于我在真实代码中遇到的更复杂场景的简单模型:

from warnings import warn
class myClass(object):        
    def __init__(self, numberArg):

        if numberArg > 1000:
            self._tmpTxt = "That's a really big number for this code." + \
                           "Code may take a while to run..."
            warn("\n%s %s" %(numberArg, self._tmpTxt))
            print("If this were real code:")
            print("Actions code takes because this is a big number would happen here.")

        print("If this were real code, it would be doing more stuff here ...")

mc1 = myClass(1001)

在我的真实代码中,当我实例化执行__init__(self, numberArg) 的类时,在警告之后的所有处理完成后,最后会输出警告。为什么会这样?

更重要的是,有没有办法确保首先输出警告,然后我的其余代码运行并提供其输出?

与此处提供的示例一样,所需的效果是在发生之前而不是之后提醒用户将要发生的事情,并像带有警告格式的警告一样提供输出。

注意:此问题是在 Windows 7 环境中的 iPython/Jupyter 上使用 Python 2.7 时遇到的

【问题讨论】:

  • warn 没有从命令行延迟。消息会发送到 stderr,也许您的环境会延迟查看该消息。
  • 我喜欢 warn() 的行为方式。它输出有问题的模块名称、代码行号,然后是您建议的粉红色警告消息格式,以提醒用户有问题。有没有办法解决这个问题?欢迎提出想法。 (只是想让我的代码更好)。注意:关于延迟理论,虽然这个例子中的代码是瞬时的,但在我的真实代码中,计算需要 3+ 分钟才能完成,最后仍然出现警告。如果是延迟,则似乎是“延迟到所有代码完成后”,而不是基于时间。
  • 刷新stderr怎么样?无论如何这会有帮助吗?
  • 这可能值得一试。在直接调用 stderr 执行此操作之前,我将检查 warn() 文档以查看是否有办法在 warn 或相关的 warn() 函数之一上执行此操作。现在正在处理我的代码中的一些问题。如果您有要发布的示例,它可以节省我的时间。否则,我对此的下一个评论可能需要一段时间。
  • @TMWP 在warn 下放置此电话:sys.stderr.flush()。如果您查看warnings 模块文档,您会发现:“是否发出警告消息的确定由警告过滤器控制,它是一系列匹配的规则和操作。” 可能是过滤器问题。先尝试刷新stderr,看看会发生什么。

标签: python class oop ipython warnings


【解决方案1】:

@direprobs 在 cmets 中为这个问题提供了最简单的答案。在调用warn()之后添加这行代码。

sys.stderr.flush()

可以将此代码复制并粘贴到 Python 2.7 (Jupyter Notebooks) 中以快速运行并查看效果:

实验一(用于与后面的代码进行比较):

# Note how warnings in this sample are held until after code is run and then output at the end ...

from warnings import warn
from warnings import resetwarnings

class myClass(object):        
    def __init__(self, numberArg):

        if numberArg > 1000:
            self._tmpTxt = "That's a really big number for this code." + \
                           "Code may take a while to run..."
            warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1, category=RuntimeWarning)
                                                       # possible categories (some of them):
                                                       # UserWarning, Warning, RunTimeWarning, ResourceWarning
                                                       # stacklevel was a experiment w/ no visible effect
                                                       # in this instance
            
            resetwarnings()                            # tried putting this before and after the warn()
            print("If this were real code:")
            print("Actions code takes because this is a big number would happen here.")
        
        print("If this were real code, it would be doing more stuff here ...")
        
mc1 = myClass(1001)

实验二:

# In this case, we want the warning to come before code execution.  This is easily fixed as shown below.
# note: removed some extraneous useless stuff, the line to look for is sys.stderr.flush()

from warnings import warn
from warnings import resetwarnings
import sys

class myClass(object):        
    def __init__(self, numberArg):

        if numberArg > 1000:
            self._tmpTxt = "That's a really big number for this code." + \
                           "Code may take a while to run..."
            warn("\n%s %s" %(numberArg, self._tmpTxt), category=Warning)            
            sys.stderr.flush()                         # put this after each warn() to make it output more immediately
            print("If this were real code:")
            print("Actions code takes because this is a big number would happen here.")
        
        print("If this were real code, it would be doing more stuff here ...")
        
mc1 = myClass(1001)  

实验三:

# code provided as an experiment ... may be updated later with a more useful example ...
# in theory, filterwarnings should help shake out repeat warnings if used with right arguments
#   * note how our loop causes the content to print twice, and in theory, the 3 instances of warnings
#   * occur twice each for 6 possible output warnings
#   * each new occurance (3 of them) still outputs, but when the same ones come up again, they don't
#   * we get 3 instead of 6 warnings ... this should be the effect of filterwarning("once")
#     in this instance

# help on this: https://docs.python.org/3/library/warnings.html#warning-filter
#               in this example:
#                  "once" arg = print only the first occurrence of matching warnings, regardless of location

from warnings import warn
from warnings import resetwarnings
from warnings import filterwarnings

class myClass(object):        
    def __init__(self, numberArg):
        
        for i in [1,2]:

            if numberArg > 1000:
                print("loop count %d:" %(i))
                self._tmpTxt = "That's a really big number for this code." + \
                               "Code may take a while to run..."
                filterwarnings("once")
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=1, category=RuntimeWarning)
                sys.stderr.flush() # this provides warning ahead of the output instead of after it
                # resetwarnings()  # no noticeable effect on the code
                print("If this were real code:")
                print("Actions code takes because this is a big number would happen here.")

            if numberArg > 20000:
                self._tmpTxt = "That's a really really really big number for this code." + \
                               "Code may take a while to run..."                
                filterwarnings("once", "\nFW: %s %s" %(numberArg, self._tmpTxt))
                warn("\n%s %s" %(numberArg, self._tmpTxt), stacklevel=0)
                # resetwarnings()  # no noticeable effect on the code
                sys.stderr.flush() # this provides warning ahead of the output instead of after it

            print("loop count %d:" %(i))    
            print("If this were real code, it would be doing more stuff here ...")

mc1 = myClass(1001)
print("====================")
mc2 = myClass(20001)

稍后在 github 上查找此代码。在此处发布以帮助其他人研究如何使用warnings

【讨论】:

  • 您仍然需要导入sys,因为模块中的导入不会导入到导入器的命名空间中。 (您可以使用warnings.sys,但文档不保证这可以正常工作。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-17
  • 1970-01-01
  • 1970-01-01
  • 2020-02-14
相关资源
最近更新 更多