【问题标题】:How can I extract keywords from a Python format string?如何从 Python 格式字符串中提取关键字?
【发布时间】:2014-11-17 18:26:27
【问题描述】:

我想在 API 中提供自动字符串格式化,这样:

my_api("path/to/{self.category}/{self.name}", ...)

可以替换为格式化字符串中调用的属性值。


如何从 Python 格式字符串中提取关键字参数:

"non-keyword {keyword1} {{escaped brackets}} {} {keyword2}" => 'keyword1', 'keyword2'

【问题讨论】:

  • 我猜你不想使用格式?
  • @user189 我使用<str>.format(**kwargs)。我的问题是关于自动构建 kwargs 字典。

标签: python string string-formatting


【解决方案1】:

您可以使用string.Formatter() class 解析出字符串中的字段,使用Formatter.parse() method

from string import Formatter

fieldnames = [fname for _, fname, _, _ in Formatter().parse(yourstring) if fname]

演示:

>>> from string import Formatter
>>> yourstring = "path/to/{self.category}/{self.name}"
>>> [fname for _, fname, _, _ in Formatter().parse(yourstring) if fname]
['self.category', 'self.name']
>>> yourstring = "non-keyword {keyword1} {{escaped brackets}} {} {keyword2}"
>>> [fname for _, fname, _, _ in Formatter().parse(yourstring) if fname]
['keyword1', 'keyword2']

您可以进一步解析这些字段名称;为此,您可以使用str._formatter_field_name_split() 方法(Python 2)/_string.formatter_field_name_split() 函数(Python 3)(此内部实现细节不会以其他方式公开;Formatter.get_field() 在内部使用它)。此函数返回名称的第一部分,将在传递给str.format() 的参数中查找的部分,以及字段其余部分的生成器。

生成器产生(is_attribute, name) 元组; is_attribute 如果将下一个名称视为属性,则为 true,如果要使用 obj[name] 查找项目,则为 false:

try:
    # Python 3
    from _string import formatter_field_name_split
except ImportError:
    formatter_field_name_split = str._formatter_field_name_split
from string import Formatter

field_references = {formatter_field_name_split(fname)[0]
 for _, fname, _, _ in Formatter().parse(yourstring) if fname}

演示:

>>> from string import Formatter
>>> from _string import formatter_field_name_split
>>> yourstring = "path/to/{self.category}/{self.name}"
>>> {formatter_field_name_split(fname)[0]
...  for _, fname, _, _ in Formatter().parse(yourstring) if fname}
{'self'}

请注意,此函数是Formatter() 类内部实现细节的一部分,可以在没有通知的情况下从 Python 中更改或删除,甚至可能在其他 Python 实现中不可用。

【讨论】:

  • 很好奇,Martijn,会使用简单的 str.replace() 或 re.sub()(或完全不同的东西)来生成新字符串吗?跨度>
  • @mtik00:取决于用例;有时str.replace() 正是我们需要的。
  • 感谢您的意见!在这种特定情况下,我会在循环中使用str.replace();又好又简单。
  • @mtik00:然而,这不是一个简单的替换工作;例如,str.format() 格式也允许 嵌套 占位符。
  • parse() 函数何时返回 None 作为字段?我无法理解文档:(
【解决方案2】:

你可以"path/to/{self.category}/{self.name}".format(self=self)。因此,您可以在 __getattr__ 中使用这些 kwargs。

【讨论】:

  • 我认为这实际上不会。
  • 你不认为这实际上会是什么?
  • @JaceBrowning:它会起作用的;将为self 上的每个属性调用self.__getattr__。但是,这对于不是 self 属性的任何内容都没有帮助。
  • @MartijnPieters 感谢您的澄清。这个答案几乎正是我所需要的,但我预计也会有非属性关键字。
【解决方案3】:

如果所有占位符都已命名,则可以使用特殊字典来拦截尝试访问哪些键并将其记录到数组中。

def format_keys(str_):
    class HelperDict(dict):
        def __init__(self):
            self._keys = []
        def __getitem__(self, key):
            self._keys.append(key)    
    d = HelperDict()
    str_.format_map(d)
    return d._keys

请注意,如果有未命名的占位符,.format() 将引发 IndexError(元组索引超出范围)。

【讨论】:

  • 一个有趣的想法,但它需要一些润色。例如__getitem__ 返回的 None 将导致来自 'My {foo} is {bar:03d}' 等格式字符串的未处理异常。
【解决方案4】:

基于 Martijn 的回答,我使用的综合列表的更简单格式是:

>>> yourstring = "path/to/{self.category}/{self.name}"
>>> [x[1] for x in yourstring._formatter_parser() if x[1]]
['self.category', 'self.name']

功能完全一样,只是更容易消化。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2016-10-15
  • 1970-01-01
  • 2021-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-07
  • 1970-01-01
相关资源
最近更新 更多