How to get named variable names from python string

Is there an elegant way to get the names of named %s variables of a string object? Like this:

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

Known alternative methods:

  • Parser names using regular expression, for example:

     import re names = re.findall(r'%\((\w)\)[sdf]', string) # ['a', 'b'] 
  • Use .format() -component formation and Formatter().parse(string) .

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

But what about a string with% s-like variables?

PS : python 2.7

+7
python string-interpolation
source share
4 answers

To answer this question, you need to define "graceful." You can think of several factors:

  • Is the code short, easy to remember, easy to write, and understandable?
  • Does it reuse logic (i.e. following the DRY principle)?
  • Does it use exactly the same parsing logic?

Unfortunately, the formatting "%" for strings is implemented in the PyString_Format subroutine in stringojbect.c. This procedure does not provide APIs or interceptors that allow access to the parsed form of the format string. It just creates a result when it parses a format string. Thus, any solution would have to duplicate the parsing logic from subroutine C. This means that DRY is not respected and provides some kind of hacking solution if changes are made to the formatting specification.

The parsing algorithm in PyString_Format involves a rather complicated task, including processing nested brackets in key names, therefore it cannot be fully implemented using a regular expression and the string "split ()" is not used. Unless you copy the C code from PyString_Format and convert it to Python code, I don’t see a remotely simple way to correctly extract the matching key names under all conditions.

So, I came to the conclusion that there is no "graceful" way to get the matching key names for the format string "%" Python 2.7.%

The following code uses a regular expression to provide a partial solution that covers the most common use:

 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'} 

PS DRY = don't repeat yourself ( https://en.wikipedia.org/wiki/Don%27t_repeat_yourself )

+3
source share

You can also do this:

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

I don't know if this qualifies as elegant in your book, but here's a short function that parses names. There is no error checking, so it will not be performed for strings with incorrect formatting.

 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
source share

Alternatively, you can reduce this % -task to Formater -solution.

 >>> 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') >>> 

In this case, you can use both formation options, I suppose.

The regular expression in it depends on what you want.

 >>> 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.' 
0
source share

All Articles