为什么'var'{1:'variable'}打印'var',而不是像'var'%(1,)那样引发异常TypeError?

2024-06-13 20:26:36 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在调试我的调试模块,我依靠一个尝试。。。catch检测TypeError并正确格式化日志,然后我注意到当传递字典时,Python不会引发传统的异常

>>> 'var' % {1: 'variable'}
'var'
>>> 'var' % (1,)
Traceback (most recent call last):
  File "<string>", line 1, in <module>

这是日志模块的一个最小示例:

import logging

class SmartLogRecord(logging.LogRecord):

    def _getMessage(self, remaining_arguments):

        try:

            if self.args:
                remaining_arguments.append( self.msg % self.args )

            else:
                remaining_arguments.append( self.msg )

            return False

        except TypeError as error:
            last = self.args[-1]
            self.args = self.args[:-1]
            remaining_arguments.append( str( last ) )

            if len( self.args ):
                return True

            else:
                remaining_arguments.append( self.msg )
                return False

    def getMessage(self):
        """
        Return the message for this LogRecord.

        Return the message for this LogRecord after merging any user-supplied
        arguments with the message.
        """
        remaining_arguments = []
        self.msg = str( self.msg )

        while self._getMessage( remaining_arguments ): pass
        return " ".join( reversed( remaining_arguments ) )

logging.setLogRecordFactory(SmartLogRecord)

var = 'SmartLogRecord'
logging.warning('I am a', var)

dumb = {1: 'variable'}
logging.warning('I am a', dumb)

运行它你会得到:

WARNING:root:I am a SmartLogRecord
WARNING:root:I am a

正如您所注意到的,最后一条dumb消息丢失了


Tags: theselfreturnvarloggingargsmsgam
2条回答

我认为观察到的行为符合docs

If format requires a single argument, values may be a single non-tuple object. [5] Otherwise, values must be a tuple with exactly the number of items specified by the format string, or a single mapping object (for example, a dictionary).

注[5]:

To format only a tuple you should therefore provide a singleton tuple whose only element is the tuple to be formatted.

这说明只有当元组完全符合格式字符串要求时,元组才被接受。一个元组包含一个本身不是元组的项,它不可能匹配任何格式字符串,并且总是引发异常

它还解释了dict总是被接受为类型,但可能会产生其他错误


在我错的情况下,有一种普遍的可能性,你刚刚发现了另一个“怪癖”。他们明确警告:

The formatting operations described here exhibit a variety of quirks that lead to a number of common errors (such as failing to display tuples and dictionaries correctly). Using the newer formatted string literals or the str.format() interface helps avoid these errors.

为了解决这个问题,我提出了以下解决方案:

import sys
import logging

if sys.version_info[0] < 3:
    is_python2 = True
    from collections import MutableMapping
else:
    from collections.abc import MutableMapping

class SmartLogRecord(logging.LogRecord):

    def _getMessage(self, remaining_arguments):

        try:
            args = self.args

            if args:

                # if isinstance( args, dict ):
                if isinstance( args, MutableMapping ):
                    new_msg = self.msg % args

                    if new_msg == self.msg:
                        remaining_arguments.append( str( args ) )
                        remaining_arguments.append( new_msg )

                    else:
                        remaining_arguments.append( new_msg )

                else:
                    remaining_arguments.append( self.msg % args )

            else:
                remaining_arguments.append( self.msg )

            return False

        except TypeError as error:
            self.args = args[:-1]
            remaining_arguments.append( str( args[-1] ) )

            if len( args ) - 1 > 0:
                return True

            else:
                remaining_arguments.append( self.msg )
                return False

    def getMessage(self):
        """
        Return the message for this LogRecord.

        Return the message for this LogRecord after merging any user-supplied
        arguments with the message.
        """
        remaining_arguments = []
        self.msg = str( self.msg )

        while self._getMessage( remaining_arguments ): pass
        return " ".join( reversed( remaining_arguments ) )

logging.setLogRecordFactory(SmartLogRecord)

var = 'SmartLogRecord'
logging.warning('I am a', var)

dumb = {1: 'variable'}
logging.warning('I am a', dumb)

正确运行:

WARNING:root:I am a SmartLogRecord
WARNING:root:I am a {1: 'variable'}

相关问题 更多 >