76

I have been examining some of my string format options using the new f-string format. I routinely need to unpack lists and other iterables of unknown length. Currently I use the following...

>>> a = [1, 'a', 3, 'b']
>>> ("unpack a list: " + " {} "*len(a)).format(*a)
'unpack a list:  1  a  3  b '

This, albeit a bit cumbersome, does the job using pre-3.6 .format notation. The new f-string format option is interesting given runtime string concatenation. It is the replication of the number of {} that I am having problems with. In my previous example, I simply created the necessary structure and unpacked within the .format() section.

Attempts to do this yielded one variant that worked, however:

1) Both curly brackets together doesn't unpack...

>>> 'unpack a list'  f' {{*a}}'
'unpack a list {*a}'

2) Adding spaces around the interior {} pair:

This works but leaves opening and closing braces {, } present:

>>> 'unpack a list'  f' { {*a} }'
"unpack a list {1, 3, 'a', 'b'}"

2b) Concatenating the variants into one f-string

This made the look and syntax better, since the evaluation, apparently, is from left to right. This, however, still left the enclosing curly brackets present:

>>> f'unpack a list { {*a} }'
"unpack a list {1, 3, 'a', 'b'}"

3) Tried automatic unpacking with just {a}

Perhaps, I was overthinking the whole procedure and hoping for some form of automatic unpacking. This simply yielded the list representation with the curly brackets being replaced with [] :

>>> f'unpack a list {a}'
"unpack a list [1, 'a', 3, 'b']"

What is required to suppress the curly brackets in variant (2) above, or must I keep using the existing .format() method? I want to keep it simple and use the new capabilities offered by the f-string and not revert back beyond the python versions which pre-date what I am currently comfortable with. I am beginning to suspect that f'strings' do not offer a complete coverage of what is offered by its .format() sibling. I will leave it at that for now, since I haven't even ventured into the escape encoding and the inability to use \ in an f-string. I have read the PEP and search widely, however, I feel I am missing the obvious or what I wish for is currently not possible.

EDIT several hours later:

4) Use subscripting to manually slice off the brackets: str(a)[1:-2]

I did find this variant which will serve for some cases that I need

f'unpack a list: {str(a)[1:-2]}'
"unpack a list: 1, 'a', 3, 'b"

But the slicing is little more than a convenience and still leaves the string quotes around the resultant.

5) and the final solution from @SenhorLucas

 a = np.arange(10)

print(f"{*a,}")
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

Unpacking with trailing comma.

NaN
  • 2,212
  • 2
  • 18
  • 23
  • 3
    FYI, in case it isn't obvious: the reason (1) doesn't work is that `{{` is how you [escape](https://www.python.org/dev/peps/pep-0498/#escape-sequences) a literal brace in an f-string, so no interpolation takes place at all in that example. – Zero Piraeus Mar 13 '17 at 05:37
  • 1
    `f"unpack a list: {', '.join([str(x) for x in [*a]])}"` returns `'unpack a list: 1, a, 3, b'` – noobninja Dec 22 '19 at 09:11
  • however " ', '.join " is more characters than "format", so you might as well stick to format ;) – NaN Dec 22 '19 at 12:23
  • 4
    I had the same reaction; but eventually I found that Python 3.6 f-strings were not intended to replace `.format()` method, but only to make the common-cases concise and add functionality; there will always be cases where you need `.format()`, and they include formatting lists/tuples/sets/arbitrary objects or nested objects. It's not a case of reverting back to pre-3.6 syntax; and `.format()` is not intended to be deprecated in favor of f-strings. Admittedly the f-string doc should explicitly say that. And also it violates *"There should be one—and preferably only one—obvious way to do it."* – smci Jan 05 '20 at 16:10
  • 4
    You are not supposed to copy other's people answer in your question. Just accept the answer and leave them the full credit for that. – divenex Aug 20 '20 at 10:36
  • 1
    In addition to @divenex comment, select an answer which actually answers your question (how to remove the braces), not one replacing braces by parentheses. – mins Sep 25 '22 at 08:18

5 Answers5

65

Shortest solution

Just add a comma after the unpacked list.

>>> a = [1, 2, 3]
>>> print(f"Unpacked list: {*a,}")
Unpacked list: (1, 2, 3)

There is a longer explanation to this syntax in this thread.

Caveat

With this solution is that we do not have much control over the output formatting. We are stuck with whatever format returns, which is actually (and suprisingly) the result from tuple.__repr__. So the parenthesis that we get might be misleading, since we actually had a list, and not a tuple.

If this is too bad to put up with, I would recommend using the approach suggested by Zero Piraeus:

>>> a = [1, 2, 3]
>>> print(f'Unpacked list: {" ".join(str(i) for i in a)}')

This gives us the flexibility to format the list as we wish.

SenhorLucas
  • 1,337
  • 12
  • 12
  • 3
    That did it, the missing comma. Thanks – NaN Jun 06 '20 at 01:09
  • 4
    note to reader: `print(*foo)` does not produce the same output as `print(f'{*foo,}')`, and it shouldn't. See link for more. – Leo Aug 29 '20 at 06:02
  • 6
    Is there a way to omit the parentheses? – Stevoisiak May 06 '22 at 17:38
  • 3
    Hello, quick question, how would I *round* the elements of the list? {*list,:.2f} doesn't seem to work. – Gerald Sep 16 '22 at 05:24
  • 4
    Why is this answer selected? `print(f"Unpacked list: {*a,}")` actually converts the list into a tuple, and unfortunately doesn't remove braces, it replaces them by parentheses and produces `Unpacked list: (1, 2, 3)`. Is there a solution actually removing the braces, shorter than this [actual answer](https://stackoverflow.com/a/42757074/774575)? – mins Sep 25 '22 at 08:15
44

Since any valid Python expression is allowed inside the braces in an f-string, you can simply use str.join() to produce the result you want:

>>> a = [1, 'a', 3, 'b']
>>> f'unpack a list: {" ".join(str(x) for x in a)}'
'unpack a list: 1 a 3 b'

You could of course also write a helper function, if your real-world use case makes the above more verbose than you'd like:

def unpack(s):
    return " ".join(map(str, s))  # map(), just for kicks

>>> f'unpack a list: {unpack(a)}'
'unpack a list: 1 a 3 b'
Zero Piraeus
  • 56,143
  • 27
  • 150
  • 160
  • 2
    @Zero_Piraeus Thanks for the suggestion, however, it still remains more inconvenient that the dot format incarnation. – NaN Mar 13 '17 at 08:28
  • 5
    Sorry to hear that … are you sure you're not just reacting negatively to an unexpected shift in the way f-strings allow you to think about the problem, though? For me, the hoops you have to jump through to construct your `format` string make it considerably less readable than the f-string alternative presented here – which uses a very common Python idiom, and is instantly obvious to anyone reading it, IMO. – Zero Piraeus Mar 13 '17 at 16:01
  • I have no problem with f-, however, I often have to unravel lists, numpy and other arguments of unknown length. This ... ("{} "*len(a)).format(*a) # a = [1,2,3,4] for example works. f- formatting equivalent couldn't be found. They both have their place. The examples in the documentation are quite simple to simplistic. If it can't be done, that is cool. In my useage little would be gained by using a function. It appears that a mix and match approach of the two works well but example (3) still left those curly brackets unfortunately... this prompted my initial question. – NaN Mar 13 '17 at 19:21
  • 2
    Assuming `a = [1, 2, 3, 4]`, if one wants to use a specific format, this works `"unpack a list: " + " ".join(f"{x:0.2f}" for x in a)`, but I agree with @NaN it not elegant. It would be nice to have a syntax like `f"unpack a list: {*a:0.2f}"` repeat the format for all list elements. I am sure I miss the reason why something like this could not be implemented... – divenex Feb 19 '18 at 18:24
1

Simple Python is probably more clear:

>>>  'unpack a list: ' + ' '.join(str(x) for x in a)
'unpack a list: 1 a 3 b'

With slicing:

>>> 'unpack a list: ' + ' '.join([str(x) for x in a][1:3])
'unpack a list: a 3'
Gringo Suave
  • 29,931
  • 6
  • 88
  • 75
  • 1
    Thanks. I am aware of the workarounds, as others have alluded to. It was this specific example... ("{} "*len(a)).format(*a) # a = [1,2,3,4] for example works. ... that an f- formatting equivalent couldn't be found. ... There is currently no implementation in f- formatting, so It is just as easy to stick with the regular route when unpacking objects of unknown size or use the join approach which preceded the ease of unpacking object introduced in later version of python 3.x – NaN Sep 28 '17 at 21:03
1

I don't think that this is the way f-Strings are meant to be used. At best I can imagine preparing a print() compatible tuple, like:

mixed = [1, "list_string", 2]
number = 23
answer = 46

info = 'Content:', *mixed, f'{number} {answer}'
print(*info)  # default sep=' '

Output

Content: 1 list_string 2 23 46
  • 1
    Ok but this assumes the OP's end-use is necessarily `print()`, not just trying to form the string representation for any other reason, e.g. `__str__()` method. – smci Jan 05 '20 at 16:31
0

I made this a while back, to include commas Oxford style.

def unpack_list(lst):  # Oxford comma
    if not isinstance(lst, str):
        lst = [str(item) for item in lst]
    if len(lst) == 0:
        return
    if len(lst) == 1:
        return ", ".join(lst)
    if len(lst) == 2:
        return ", and ".join(lst) 
    else:
        first_part = lst[:-1]
        last_part = lst[-1]
        return ", ".join(first_part) + ", and " + last_part
Lewi Uberg
  • 1,055
  • 7
  • 14