python 字符串格式 抑制/静默 keyerror/indexerror

发布于 2024-09-15 04:58:03 字数 705 浏览 3 评论 0原文

有没有一种方法可以使用 python string.format ,以便在缺少索引时不会引发异常,而是插入空字符串。

result = "i am an {error} example string {error2}".format(hello=2,error2="success")

这里,结果应该是:

"i am an   example string success"

现在,python 抛出一个关键错误并停止格式化。有可能改变这种行为吗?

谢谢

编辑:

存在 Template.safe_substitute (即使保留模式完整而不是插入空字符串),但对于 string.format 不能有类似的东西

所需的行为将类似于 php 中的字符串替换。

class Formatter(string.Formatter):
  def get_value(self,key,args,kwargs):
    try:
        if hasattr(key,"__mod__"):
            return args[key]
        else:
            return kwargs[key]
    except:
        return ""

这似乎提供了所需的行为。

Is there a way to use python string.format such that no exception is thrown when an index is missing, instead an empty string is inserted.

result = "i am an {error} example string {error2}".format(hello=2,error2="success")

here,result should be :

"i am an   example string success"

Right now, python throws a keyerror and stops formatting. Is it possible to change this behavior ?

Thanks

Edit:

There exists Template.safe_substitute (even that leaves the pattern intact instead of inserting an empty string) , but couldn't something similar for string.format

The desired behavior would be similar to string substitution in php.

class Formatter(string.Formatter):
  def get_value(self,key,args,kwargs):
    try:
        if hasattr(key,"__mod__"):
            return args[key]
        else:
            return kwargs[key]
    except:
        return ""

This seems to provide the desired behavior.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

晚雾 2024-09-22 04:58:03

格式映射中字符串的官方解决方案(Python 3 Docs)是子类化dict类并定义魔术方法__missing__()。每当缺少键时就会调用此方法,并且它返回的内容将用于字符串格式化:

class format_dict(dict):
    def __missing__(self, key):
        return "..."

d = format_dict({"foo": "name"})

print("My %(foo)s is %(bar)s" % d) # "My name is ..."

print("My {foo} is {bar}".format(**d)) # "My name is ..."

编辑:第二个 print() 在 Python 3.5.3 中有效,但在例如 3.7 中无效.2: KeyError: 'bar' 被引发,我找不到捕获它的方法。

经过一些实验,我发现 Python 的行为有所不同。在 v3.5.3 中,调用成功的是 __getitem__(self, "foo") ,而 __getitem__(self, "bar") 则找不到密钥 "bar",因此它调用 __missing__(self, "bar") 来处理丢失的键,而不抛出 KeyError。在 v3.7.2 中,内部调用 __getattribute__(self, "keys")。内置的 keys() 方法用于返回键上的迭代器,该迭代器产生“foo”,__getitem__("foo") 成功,则迭代器为筋疲力尽的。对于格式字符串中的 {bar},没有键 "bar"__getitem__()__missing_() 不会被调用来处理这种情况。相反,会抛出 KeyError。我不知道如何才能抓住它(如果有的话)。

在 Python 3.2+ 中,您应该使用 format_map() 代替(另请参阅 Python Bug Tracker - 问题 6081):

from collections import defaultdict
    
d = defaultdict(lambda: "...")
d.update({"foo": "name"})

print("My {foo} is {bar}".format_map(d)) # "My name is ..."

如果您想保留占位符,您可以这样做:

class Default(dict):
    def __missing__(self, key): 
        return key.join("{}")
    
d = Default({"foo": "name"})

print("My {foo} is {bar}".format_map(d)) # "My name is {bar}"

如您所见,format_map() 确实调用 <代码>__missing__()。

以下似乎是最兼容的解决方案,因为它也适用于包括 2.x 在内的旧版 Python 版本(我测试了 v2.7.15):

class Default(dict):
    def __missing__(self, key):
        return key.join("{}")

d = Default({"foo": "name"})

import string
print(string.Formatter().vformat("My {foo} is {bar}", (), d)) # "My name is {bar}"

保持占位符原样包括格式规范(例如 {bar:<15}) 需要对格式化程序进行子类化:

import string

class Unformatted:
    def __init__(self, key):
        self.key = key
    def __format__(self, format_spec):
        return "{{{}{}}}".format(self.key, ":" + format_spec if format_spec else "")

class Formatter(string.Formatter):
    def get_value(self, key, args, kwargs):
        if isinstance(key, int):
            try:
                return args[key]
            except IndexError:
                return Unformatted(key)
        else:
            try:
                return kwargs[key]
            except KeyError:
                return Unformatted(key)


f = Formatter()
s1 = f.vformat("My {0} {1} {foo:<10} is {bar:<15}!", ["real"], {"foo": "name"})
s2 = f.vformat(s1, [None, "actual"], {"bar":"Geraldine"})
print(s1) # "My real {1} name       is {bar:<15}!"
print(s2) # "My real actual name       is Geraldine      !"

请注意,占位符索引不会更改({1} 保留在字符串中,而没有 {0 }),为了替换 {1},您需要传递一个包含任何奇数第一个元素的数组以及您想要替换剩余占位符作为第二个元素的数组(例如 [无,“实际”])。

您还可以使用位置参数和命名参数调用 format() 方法:

s1 = f.format("My {0} {1} {foo:<10} is {bar:<15}!", "real", foo="name")
s2 = f.format(s1, None, "actual", bar="Geraldine")

The official solution (Python 3 Docs) for strings in format mappings is to subclass the dict class and to define the magic-method __missing__(). This method is called whenever a key is missing, and what it returns is used for the string formatting instead:

class format_dict(dict):
    def __missing__(self, key):
        return "..."

d = format_dict({"foo": "name"})

print("My %(foo)s is %(bar)s" % d) # "My name is ..."

print("My {foo} is {bar}".format(**d)) # "My name is ..."

Edit: the second print() works in Python 3.5.3, but it does not in e.g. 3.7.2: KeyError: 'bar' is raised and I couldn't find a way to catch it.

After some experiments, I found a difference in Python's behavior. In v3.5.3, the calls are __getitem__(self, "foo") which succeeds and __getitem__(self, "bar") which can not find the key "bar", therefore it calls __missing__(self, "bar") to handle the missing key without throwing a KeyError. In v3.7.2, __getattribute__(self, "keys") is called internally. The built-in keys() method is used to return an iterator over the keys, which yields "foo", __getitem__("foo") succeeds, then the iterator is exhausted. For {bar} from the format string there is no key "bar". __getitem__() and hence __missing_() are not called to handle the situation. Instead, the KeyError is thrown. I don't know how one could catch it, if at all.

In Python 3.2+ you should use format_map() instead (also see Python Bug Tracker - Issue 6081):

from collections import defaultdict
    
d = defaultdict(lambda: "...")
d.update({"foo": "name"})

print("My {foo} is {bar}".format_map(d)) # "My name is ..."

If you want to keep the placeholders, you can do:

class Default(dict):
    def __missing__(self, key): 
        return key.join("{}")
    
d = Default({"foo": "name"})

print("My {foo} is {bar}".format_map(d)) # "My name is {bar}"

As you can see, format_map() does call __missing__().

The following appears to be the most compatible solution as it also works in older Python versions including 2.x (I tested v2.7.15):

class Default(dict):
    def __missing__(self, key):
        return key.join("{}")

d = Default({"foo": "name"})

import string
print(string.Formatter().vformat("My {foo} is {bar}", (), d)) # "My name is {bar}"

To keep placeholders as-is including the format spec (e.g. {bar:<15}) the Formatter needs to be subclassed:

import string

class Unformatted:
    def __init__(self, key):
        self.key = key
    def __format__(self, format_spec):
        return "{{{}{}}}".format(self.key, ":" + format_spec if format_spec else "")

class Formatter(string.Formatter):
    def get_value(self, key, args, kwargs):
        if isinstance(key, int):
            try:
                return args[key]
            except IndexError:
                return Unformatted(key)
        else:
            try:
                return kwargs[key]
            except KeyError:
                return Unformatted(key)


f = Formatter()
s1 = f.vformat("My {0} {1} {foo:<10} is {bar:<15}!", ["real"], {"foo": "name"})
s2 = f.vformat(s1, [None, "actual"], {"bar":"Geraldine"})
print(s1) # "My real {1} name       is {bar:<15}!"
print(s2) # "My real actual name       is Geraldine      !"

Note that the placeholder indices are not changed ({1} remains in the string without a {0}), and in order to substitute {1} you need to pass an array with any odd first element and what you want to substitute the remaining placeholder with as second element (e.g. [None, "actual"]).

You can also call the format() method with positional and named arguments:

s1 = f.format("My {0} {1} {foo:<10} is {bar:<15}!", "real", foo="name")
s2 = f.format(s1, None, "actual", bar="Geraldine")
岁月打碎记忆 2024-09-22 04:58:03

str.format() 不需要映射对象。试试这个:

from collections import defaultdict

d = defaultdict(str)
d['error2'] = "success"
s = "i am an {0[error]} example string {0[error2]}"
print s.format(d)

您使用返回“”的 str() 工厂创建一个 defaultdict。然后你为 defaultdict 创建一个键。在格式字符串中,您可以访问传递的第一个对象的键。这样做的优点是允许您传递其他键和值,只要您的 defaultdict 是 format() 的第一个参数。

另请参阅 http://bugs.python.org/issue6081

str.format() doesn't expect a mapping object. Try this:

from collections import defaultdict

d = defaultdict(str)
d['error2'] = "success"
s = "i am an {0[error]} example string {0[error2]}"
print s.format(d)

You make a defaultdict with a str() factory that returns "". Then you make one key for the defaultdict. In the format string, you access keys of the first object passed. This has the advantage of allowing you to pass other keys and values, as long as your defaultdict is the first argument to format().

Also, see http://bugs.python.org/issue6081

不即不离 2024-09-22 04:58:03

不幸的是,不,默认情况下没有这样的方法。但是,您可以为其提供 defaultdict 或具有覆盖的 __getattr__ 的对象,并像这样使用:

class SafeFormat(object):
    def __init__(self, **kw):
        self.__dict = kw

    def __getattr__(self, name):
        if not name.startswith('__'):
            return self.__dict.get(name, '')

print "i am an {0.error} example string {0.error2}".format(SafeFormat(hello=2,error2="success"))
i am an  example string success

Unfortunately, no, there is no such way to do by default. However you can provide it defaultdict or object with overridden __getattr__, and use like this:

class SafeFormat(object):
    def __init__(self, **kw):
        self.__dict = kw

    def __getattr__(self, name):
        if not name.startswith('__'):
            return self.__dict.get(name, '')

print "i am an {0.error} example string {0.error2}".format(SafeFormat(hello=2,error2="success"))
i am an  example string success
初熏 2024-09-22 04:58:03

我制作了一个与 Daniel 的方法类似的版本,但没有 {0.x} 属性访问权限。

import string    
class SafeFormat(object):
    def __init__(self, **kw):
        self.__dict = kw

    def __getitem__(self, name):
        return self.__dict.get(name, '{%s}' % name)



string.Formatter().vformat('{what} {man}', [], SafeFormat(man=2))

打印出来

'{what} 2'

I made a version that does work similarly to Daniel's method but without the {0.x} attribute access.

import string    
class SafeFormat(object):
    def __init__(self, **kw):
        self.__dict = kw

    def __getitem__(self, name):
        return self.__dict.get(name, '{%s}' % name)



string.Formatter().vformat('{what} {man}', [], SafeFormat(man=2))

prints out

'{what} 2'
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文