【问题标题】:Get keys from template从模板中获取密钥
【发布时间】:2019-03-18 20:42:42
【问题描述】:

我想获得一个字符串模板在替换中可能使用的所有可能关键字参数的列表。

除了re还有其他方法吗?

我想做这样的事情:

text="$one is a $lonely $number."
keys = get_keys(text) 
# keys = ('one', 'lonely', 'number')

我正在编写一个简单的 Mad-lib 类程序,我想使用 string.format 或 Template strings 执行模板替换。我想编写“故事”并让我的程序生成用户需要生成的所有“关键字”(名词、动词等)的模板文件。我知道我可以用正则表达式做到这一点,但我想知道是否有替代解决方案?我对 string.format 和 string template 的替代品持开放态度。

我认为会有解决方案,但我没有在快速搜索中找到它。我确实找到了这个问题,reverse template with python,但这并不是我真正想要的。它只是重申这可以通过re 完成。

编辑:

我应该注意$$ 是“$”的转义,而不是我想要的令牌。 $$5 应该呈现为“$5”。

【问题讨论】:

    标签: python string-formatting


    【解决方案1】:

    如果可以使用string.format,请考虑使用具有parse() 方法的内置类string.Formatter:

    >>> from string import Formatter
    >>> [i[1] for i in Formatter().parse('Hello {1} {foo}')  if i[1] is not None]
    ['1', 'foo']
    

    更多详情请见here。

    【讨论】:

    • 几乎是我想要的。也许我的问题需要解决,但我基本上不想重新发明轮子。谢谢。
    • 如果字符串以键开头,这不起作用。试试上面的{foo} test
    • @syntacticmarmalade 在 Python 3.6 上对我来说效果很好。如果您的意思是列表末尾有一个None,那是因为该字符串没有end 带有键。您可以过滤掉None 的实例。
    【解决方案2】:

    string.Template 类具有用作属性的模式。您可以打印模式以获取匹配组

    >>> print string.Template.pattern.pattern
    
        \$(?:
          (?P<escaped>\$) |   # Escape sequence of two delimiters
          (?P<named>[_a-z][_a-z0-9]*)      |   # delimiter and a Python identifier
          {(?P<braced>[_a-z][_a-z0-9]*)}   |   # delimiter and a braced identifier
          (?P<invalid>)              # Other ill-formed delimiter exprs
        )
    

    对于你的例子,

    >>> string.Template.pattern.findall("$one is a $lonely $number.")
    [('', 'one', '', ''), ('', 'lonely', '', ''), ('', 'number', '', '')]
    

    如您在上面看到的,如果您使用大括号执行${one},它将转到结果元组的第三位:

    >>> string.Template.pattern.findall('${one} is a $lonely $number.')
    [('', '', 'one', ''), ('', 'lonely', '', ''), ('', 'number', '', '')]
    

    因此,如果您想获得所有密钥,则必须执行以下操作:

    >>> [s[1] or s[2] for s in string.Template.pattern.findall('${one} is a $lonely $number.$$') if s[1] or s[2]]
    ['one', 'lonely', 'number']
    

    【讨论】:

      【解决方案3】:

      您可以使用记录调用的检测字典或默认字典将其渲染一次,然后检查它要求的内容。

      from collections import defaultdict
      d = defaultdict("bogus")
      text%d
      keys = d.keys()
      

      【讨论】:

        【解决方案4】:

        试试str.strip() 和str.split():

        In [54]: import string
        
        In [55]: text="$one is a $lonely $number."
        
        In [56]: [x.strip(string.punctuation) for x in text.split() if x.startswith("$")]
        Out[56]: ['one', 'lonely', 'number']
        

        【讨论】:

        • $** 是 **string.punctuation** 的一部分,使得 **lstrip('$') 多余
        【解决方案5】:

        你可以试试:

        def get_keys(s):
            tokens = filter(lambda x: x[0] == "$", s.split())
            return map(lambda x: x[1:], tokens)
        

        【讨论】:

          【解决方案6】:

          为什么要避免使用正则表达式?他们在这方面工作得很好:

          >>> re.findall(r'\$[a-z]+', "$one is a $lonely $number.")
          ['$one', '$lonely', '$number']
          

          对于模板,请查看re.sub,它可以通过回调调用来做几乎你想做的事情。

          【讨论】:

          • 我不想避免使用正则表达式,我只是想知道是否还有其他方法。
          • 好吧,如果你想要一只脖子很长的非洲动物,当然可以拉伸鳄鱼,但在大多数情况下,搭配长颈鹿会更容易。
          【解决方案7】:
          >>> import string
          >>> get_keys = lambda s:[el.strip(string.punctuation) 
                                   for el in s.split()if el.startswith('$')]
          >>> get_keys("$one is a $lonely $number.")
          ['one', 'lonely', 'number']
          

          【讨论】:

            猜你喜欢
            • 2013-10-06
            • 2016-10-11
            • 1970-01-01
            • 2018-02-08
            • 2012-03-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-06-28
            相关资源
            最近更新 更多