String formatting with the format string syntax

Discussion in 'Python' started by Andre Alexander Bell, Sep 14, 2010.

  1. Hello,

    I'm used to write in Python something like

    >>> s = 'some text that says: %(hello)s'


    and then have a dictionary like

    >>> english = { 'hello': 'hello' }


    and get the formatted output like this:

    >>> s % english


    Occasionally I want to extract the field names from the template string.
    I was used to write a class like

    class Extractor(object):
    def __init__(self):
    self.keys = []
    def __getitem__(self, key):
    self.keys.append(key)
    return ''

    and use it like this:

    >>> e = Extractor()
    >>> res = s % e
    >>> e.keys

    ['hello']

    Now Python has the format method for string formatting with the more
    advanced handling. So I could as well write

    >>> s = 'some text that says: {hello!s}'
    >>> s.format(hello='hello')


    My question is, if I do have a string template which uses the newer
    format string syntax, how do I best extract the field information?

    I found the str._formatter_parser() method which I could use like this:

    keys = []
    for (a, key, c, d) in s._formatter_parser():
    if key:
    keys.append(key)

    Is there a more elegant solution?
    What are a, c, d?
    Where can I find additional information on this method?
    Should one use a method that actually starts with an _?
    Couldn't this one change any time soon?

    Thanks for any help


    Andre
     
    Andre Alexander Bell, Sep 14, 2010
    #1
    1. Advertising

  2. Andre Alexander Bell

    Miki Guest

    You can use ** syntax:
    >>> english = {'hello':'hello'}
    >>> s.format(**english)


    On Sep 14, 9:59 am, Andre Alexander Bell <> wrote:
    > Hello,
    >
    > I'm used to write in Python something like
    >
    >  >>> s = 'some text that says: %(hello)s'
    >
    > and then have a dictionary like
    >
    >  >>> english = { 'hello': 'hello' }
    >
    > and get the formatted output like this:
    >
    >  >>> s % english
    >
    > Occasionally I want to extract the field names from the template string.
    > I was used to write a class like
    >
    > class Extractor(object):
    >      def __init__(self):
    >          self.keys = []
    >      def __getitem__(self, key):
    >          self.keys.append(key)
    >          return ''
    >
    > and use it like this:
    >
    >  >>> e = Extractor()
    >  >>> res = s % e
    >  >>> e.keys
    > ['hello']
    >
    > Now Python has the format method for string formatting with the more
    > advanced handling. So I could as well write
    >
    >  >>> s = 'some text that says: {hello!s}'
    >  >>> s.format(hello='hello')
    >
    > My question is, if I do have a string template which uses the newer
    > format string syntax, how do I best extract the field information?
    >
    > I found the str._formatter_parser() method which I could use like this:
    >
    > keys = []
    > for (a, key, c, d) in s._formatter_parser():
    >      if key:
    >          keys.append(key)
    >
    > Is there a more elegant solution?
    > What are a, c, d?
    > Where can I find additional information on this method?
    > Should one use a method that actually starts with an _?
    > Couldn't this one change any time soon?
    >
    > Thanks for any help
    >
    > Andre
     
    Miki, Sep 14, 2010
    #2
    1. Advertising

  3. On Tuesday 14 September 2010, it occurred to Miki to exclaim:
    > You can use ** syntax:
    > >>> english = {'hello':'hello'}
    > >>> s.format(**english)


    No, you can't. This only works with dicts, not with arbitrary mappings, or
    dict subclasses that try to do some kind of funny stuff.

    >
    > On Sep 14, 9:59 am, Andre Alexander Bell <> wrote:
    > > Hello,
    > >
    > > I'm used to write in Python something like
    > >
    > > >>> s = 'some text that says: %(hello)s'

    > >
    > > and then have a dictionary like
    > >
    > > >>> english = { 'hello': 'hello' }

    > >
    > > and get the formatted output like this:
    > >
    > > >>> s % english

    > >
    > > Occasionally I want to extract the field names from the template string.
    > > I was used to write a class like
    > >
    > > class Extractor(object):
    > > def __init__(self):
    > > self.keys = []
    > > def __getitem__(self, key):
    > > self.keys.append(key)
    > > return ''
    > >
    > > and use it like this:
    > >
    > > >>> e = Extractor()
    > > >>> res = s % e
    > > >>> e.keys

    > > ['hello']
    > >
    > > Now Python has the format method for string formatting with the more
    > > advanced handling. So I could as well write
    > >
    > > >>> s = 'some text that says: {hello!s}'
    > > >>> s.format(hello='hello')

    > >
    > > My question is, if I do have a string template which uses the newer
    > > format string syntax, how do I best extract the field information?
    > >
    > > I found the str._formatter_parser() method which I could use like this:
    > >
    > > keys = []
    > > for (a, key, c, d) in s._formatter_parser():
    > > if key:
    > > keys.append(key)
    > >
    > > Is there a more elegant solution?
    > > What are a, c, d?
    > > Where can I find additional information on this method?
    > > Should one use a method that actually starts with an _?
    > > Couldn't this one change any time soon?
    > >
    > > Thanks for any help
    > >
    > > Andre
     
    Thomas Jollans, Sep 14, 2010
    #3
  4. On Tue, Sep 14, 2010 at 3:20 PM, Thomas Jollans <> wrote:
    > On Tuesday 14 September 2010, it occurred to Miki to exclaim:
    >> You can use ** syntax:
    >> >>> english = {'hello':'hello'}
    >> >>> s.format(**english)

    >
    > No, you can't. This only works with dicts, not with arbitrary mappings, or
    > dict subclasses that try to do some kind of funny stuff.
    >


    That was changed in 2.6
    http://docs.python.org/release/2.6.6/whatsnew/2.6.html#other-language-changes
     
    Benjamin Kaplan, Sep 14, 2010
    #4
  5. On 09/14/2010 08:20 PM, Miki wrote:
    > You can use ** syntax:
    >>>> english = {'hello':'hello'}
    >>>> s.format(**english)


    Thanks for your answer. Actually your answer tells me that my example
    was misleading. Consider the template

    s = 'A template with {variable1} and {variable2} placeholders.'

    I'm seeking a way to extract the named placesholders, i.e. the names
    'variable1' and 'variable2' from the template. I'm not trying to put in
    values for them.

    I hope this is clearer.

    Thanks again


    Andre
     
    Andre Alexander Bell, Sep 15, 2010
    #5
  6. Andre Alexander Bell

    Peter Otten Guest

    Andre Alexander Bell wrote:

    > On 09/14/2010 08:20 PM, Miki wrote:
    >> You can use ** syntax:
    >>>>> english = {'hello':'hello'}
    >>>>> s.format(**english)

    >
    > Thanks for your answer. Actually your answer tells me that my example
    > was misleading. Consider the template
    >
    > s = 'A template with {variable1} and {variable2} placeholders.'
    >
    > I'm seeking a way to extract the named placesholders, i.e. the names
    > 'variable1' and 'variable2' from the template. I'm not trying to put in
    > values for them.
    >
    > I hope this is clearer.


    >>> s = 'A template with {variable1} and {variable2} placeholders.'
    >>> [name for _, name, _, _ in s._formatter_parser() if name is not None]

    ['variable1', 'variable2']

    Peter
     
    Peter Otten, Sep 15, 2010
    #6
  7. Andre Alexander Bell

    Peter Otten Guest

    Peter Otten wrote:

    > Andre Alexander Bell wrote:
    >
    >> On 09/14/2010 08:20 PM, Miki wrote:
    >>> You can use ** syntax:
    >>>>>> english = {'hello':'hello'}
    >>>>>> s.format(**english)

    >>
    >> Thanks for your answer. Actually your answer tells me that my example
    >> was misleading. Consider the template
    >>
    >> s = 'A template with {variable1} and {variable2} placeholders.'
    >>
    >> I'm seeking a way to extract the named placesholders, i.e. the names
    >> 'variable1' and 'variable2' from the template. I'm not trying to put in
    >> values for them.
    >>
    >> I hope this is clearer.

    >
    >>>> s = 'A template with {variable1} and {variable2} placeholders.'
    >>>> [name for _, name, _, _ in s._formatter_parser() if name is not None]

    > ['variable1', 'variable2']


    Caveat: the format spec may contain names, too.
    Here's an attempt to take that into account:

    def extract_names(t, recurse=1):
    for _, name, fmt, _ in t._formatter_parser():
    if name is not None:
    yield name
    if recurse and fmt is not None:
    for name in extract_names(fmt, recurse-1):
    yield name

    t = "before {one:{two}{three}} after"
    print(t)

    for name in extract_names(t):
    print(name)

    >>> list(extract_names("{one:{two}{three}}"))

    ['one', 'two', 'three']

    Don't expect correct results for illegal formats:
    >>> list(extract_names("{one:{two:{three}}}"))

    ['one', 'two']
    >>> "{one:{two:{three}}}".format(one=1, two=2, three=3)

    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    ValueError: Max string recursion exceeded

    Duplicate names may occur:
    >>> list(extract_names("{one} {one} {one}"))

    ['one', 'one', 'one']

    Positional arguments are treated like names:
    >>> list(extract_names("{0} {1} {0}"))

    ['0', '1', '0']
    >>> list(extract_names("{} {} {}"))

    ['', '', '']

    Peter
     
    Peter Otten, Sep 15, 2010
    #7
  8. On 09/15/2010 10:00 AM, Peter Otten wrote:
    > def extract_names(t, recurse=1):
    > for _, name, fmt, _ in t._formatter_parser():
    > if name is not None:
    > yield name
    > if recurse and fmt is not None:
    > for name in extract_names(fmt, recurse-1):
    > yield name


    Thanks Peter, I very much like this generator solution. It will work for
    all situations I can currently think of.

    However, one thing remains. It is based on the _format_parser method.
    And as I wrote in my original post this one - since it starts with _ -
    suggests to me to better not be used. So if using this method is
    completely ok, why does it start with _, why is it almost undocumented?
    Or did I miss something, some docs somewhere?

    Best regards


    Andre
     
    Andre Alexander Bell, Sep 15, 2010
    #8
  9. Andre Alexander Bell

    Peter Otten Guest

    Andre Alexander Bell wrote:

    > On 09/15/2010 10:00 AM, Peter Otten wrote:
    >> def extract_names(t, recurse=1):
    >> for _, name, fmt, _ in t._formatter_parser():
    >> if name is not None:
    >> yield name
    >> if recurse and fmt is not None:
    >> for name in extract_names(fmt, recurse-1):
    >> yield name

    >
    > Thanks Peter, I very much like this generator solution. It will work for
    > all situations I can currently think of.
    >
    > However, one thing remains. It is based on the _format_parser method.
    > And as I wrote in my original post this one - since it starts with _ -
    > suggests to me to better not be used. So if using this method is
    > completely ok, why does it start with _, why is it almost undocumented?
    > Or did I miss something, some docs somewhere?


    Sorry, I really should have read your original post carefully/completely. It
    would have spared me from finding _formatter_parser() independently...

    I personally would not be too concerned about the leading underscore, but
    you can use

    string.Formatter().parse(template)

    instead.

    Peter
     
    Peter Otten, Sep 15, 2010
    #9
  10. On 09/15/2010 10:48 AM, Peter Otten wrote:
    > I personally would not be too concerned about the leading underscore, but
    > you can use
    >
    > string.Formatter().parse(template)
    >
    > instead.


    Thanks for this pointer. I like it this way. So if I now combine your
    generator with your suggestion, I end up with something like this:

    def extract_names(t, recurse=1):
    import string
    for _, name, fmt, _ in string.Formatter().parse(t):
    if name is not None:
    yield name
    if recurse and fmt is not None:
    for name in extract_names(fmt, recurse-1):
    yield name

    Pretty cool. Thanks a lot.

    Andre
     
    Andre Alexander Bell, Sep 15, 2010
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Brian Candy
    Replies:
    2
    Views:
    1,205
    Janaka
    Feb 18, 2004
  2. Chris Angelico
    Replies:
    3
    Views:
    178
    Mark Lawrence
    Mar 1, 2013
  3. Peter Otten
    Replies:
    0
    Views:
    149
    Peter Otten
    Feb 28, 2013
  4. Rick Johnson
    Replies:
    0
    Views:
    154
    Rick Johnson
    Feb 28, 2013
  5. Victor Hooi
    Replies:
    3
    Views:
    145
    Chris Kaynor
    Nov 27, 2013
Loading...

Share This Page