2016-01-19 154 views
7

有没有一种优美的方式来获取名称为%s的字符串对象的变量? 像这样:如何从python字符串获取命名变量的名称

string = '%(a)s and %(b)s are friends.' 
names = get_names(string) # ['a', 'b'] 

已知的替代方法:

  1. 解析名称中使用正则表达式,例如:

    import re 
    names = re.findall(r'%\((\w)\)[sdf]', string) # ['a', 'b'] 
    
  2. 使用.format()兼容的格式化和Formatter().parse(string)

    How to get the variable names from the string for the format() method

但对于与%s类变量的字符串?

PS:蟒蛇2.7

+2

您所描述的方法似乎效果不错。它返回['a','b']。那么现在失去了什么? –

+0

@AdiLevin第一种方式需要额外的导入。第二种方式需要另一种字符串格式。我只是很好奇,有没有一种方法可以只使用'string'对象的内部方法和属性或者可能是一些字符串模块函数来获得相同的结果。 – hackprime

+0

是什么阻止你使用'format()进行格式化?这看起来就像是其中一个更强大的案例。 – Joost

回答

0

你也可以这样做:

[y[0] for y in [x.split(')') for x in s.split('%(')] if len(y)>1] 
+0

就像问题中的正则表达式,这在'%%(a)s''上失败。 – BlackJack

+0

那么确切的要求是什么?除了%(a)s之外,我们还需要解析哪些其他类型的表达式? %%(如?还要别的吗? –

0

不知道这是否有资格在你的书当作美丽的,但这里的一个分析出来的名字很短的函数。没有错误检查,所以它会失败的格式错误的字符串。

def get_names(s): 
    i = s.find('%') 
    while 0 <= i < len(s) - 3: 
     if s[i+1] == '(': 
      yield(s[i+2:s.find(')', i)]) 
     i = s.find('%', i+2) 

string = 'abd %(one) %%(two) 99 %%%(three)' 
list(get_names(string) #=> ['one', 'three'] 
0

此外,您还可以在此% -task减少Formater - 溶液。

>>> import re 
>>> from string import Formatter 
>>> 
>>> string = '%(a)s and %(b)s are friends.' 
>>> 
>>> string = re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', string) 
>>> 
>>> tuple(fn[1] for fn in Formatter().parse(string) if fn[1] is not None) 
('a', 'b') 
>>> 

在这种情况下,你可以使用两种形式的变体,我想。

其中的正则表达式取决于你想要的。

>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %(c)s friends.') 
'{a} and {b} are {c} friends.' 
>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %%(c)s friends.') 
'{a} and {b} are %%(c)s friends.' 
>>> re.sub('((?<!%)%(\((\w)\)s))', '{\g<3>}', '%(a)s and %(b)s are %%%(c)s friends.') 
'{a} and {b} are %%%(c)s friends.' 
3

为了回答这个问题,您需要定义“优雅”。有几个因素值得考虑:

  1. 代码简短,易于记忆,易于编写和自我解释?
  2. 它是否重用底层逻辑(即遵循DRY原则)?
  3. 它是否实现了完全相同的解析逻辑?

不幸的是,字符串的“%”格式是在stringojbect.c中的C例程“PyString_Format”中实现的。此例程不提供允许访问格式字符串的已分析格式的API或挂钩。它只是在解析格式字符串时生成结果。因此,任何解决方案都需要复制C例程中的解析逻辑。这意味着DRY没有被遵循,并且如果对格式化规范进行了更改,则会暴露任何解决方案。

PyString_Format中的解析算法包含一定的复杂性,包括处理键名中的嵌套圆括号,所以不能使用正则表达式完全实现,也不能使用字符串“split()”。由于没有从PyString_Format复制C代码并将其转换为Python代码,我没有看到任何远程简单的方法,在所有的情况下正确提取映射密钥的名称。

所以我的结论是,没有“优雅”的方式来获取Python 2.7“%”格式字符串的映射关键字的名称。

以下代码使用正则表达式来提供覆盖最常见的用法的部分解决方案:

import re 
class StringFormattingParser(object): 
    __matcher = re.compile(r'(?<!%)%\(([^)]+)\)[-# +0-9.hlL]*[diouxXeEfFgGcrs]') 
    @classmethod 
    def getKeyNames(klass, formatString): 
     return klass.__matcher.findall(formatString) 

# Demonstration of use with some sample format strings 
for value in [ 
    '%(a)s and %(b)s are friends.', 
    '%%(nomatch)i', 
    '%%', 
    'Another %(matched)+4.5f%d%% example', 
    '(%(should_match(but does not))s', 
    ]: 
    print StringFormattingParser.getKeyNames(value) 

# Note the following prints out "really does match"! 
print '%(should_match(but does not))s' % {'should_match(but does not)': 'really does match'} 

P.S. DRY =不要重复自己(https://en.wikipedia.org/wiki/Don%27t_repeat_yourself