5

Recently, I found ''.format function very useful because it can improve readability a lot comparing to the % formatting. Trying to achieve simple string formatting:

data = {'year':2012, 'month':'april', 'location': 'q2dm1'}

year = 2012
month = 'april'
location = 'q2dm1'
a = "year: {year}, month: {month}, location: {location}"
print a.format(data)
print a.format(year=year, month=month, location=location)
print a.format(year, month, location)

Whilst two first prints do format as I expect (yes, something=something looks ugly, but that's an example only), the last one would raise KeyError: 'year'. Is there any trick in python to create dictionary so it will automatically fill keys and values, for example somefunc(year, month, location) will output {'year':year, 'month': month, 'location': location}?

I'm pretty new to python and couldn't find any info on this topic, however a trick like this would improve and shrink my current code drastically.

Thanks in advance and pardon my English.

icecrime
  • 74,451
  • 13
  • 99
  • 111
duke_nukem
  • 647
  • 2
  • 9
  • 16

5 Answers5

6

The first print should be

print a.format(**data)

Also, if you are finding some shortcuts, you could write one like, no big difference.

def trans(year, month, location):
    return dict(year=year, month=month, location=location)
okm
  • 23,575
  • 5
  • 83
  • 90
  • Seems there's no way to shrink this repeating code:) Function is good and it was what i did, actually, but later i found it hard to sustain because with lots of arguments i needed to change code in several places, which turned out to be even worse than actually creating dict by hands. – duke_nukem Apr 15 '12 at 08:09
  • @duke_nukem yes it depends on your actual usage. The code is fine and the format api is clear enough. If you what to achieve something like `format(*args, **kwargs)`, it could be done also, but I don't think it could make things easier. – okm Apr 15 '12 at 08:15
3
data = {'year':2012, 'month':'april', 'location': 'q2dm1'}
a = "year: {year}, month: {month}, location: {location}"

print a.format(**data)

..is what you are looking for. It's functionally identical to doing .format(year=data['year'], ...), or the other examples you gave.

The double-asterix thing is a hard thing to search for, so it's usually referred to as "kwargs". Here's a good SO question on this syntax

Community
  • 1
  • 1
dbr
  • 165,801
  • 69
  • 278
  • 343
1

You can use the dict() callable:

dict(year=yeah, month=month, location=location)

When passing keyword arguments it creates a dict containing the elements you specified as kwargs.

If you do not want to specify the argument names, use the positional style of .format():

>>> a = 'year {0} month {1} location {2}'
>>> print a.format(2012, 'april', 'abcd')
year 2012 month april location abcd

However, if you try to do something similar to what compact() in PHP does (create a dict mapping variable names to its values without specifying name and variable separately), please don't. It just results in ugly unreadable code and would require nasty hacks anyway.

ThiefMaster
  • 310,957
  • 84
  • 592
  • 636
  • Yes, i'm aware of dict() but i'm trying to avoid unnecessary code and shrink it down a bit. .format can accept your option without dict() by the way. Thank you. – duke_nukem Apr 15 '12 at 07:56
  • Thanks, it's close to what i need, however with 10-12 arguments it becomes pretty hard to navigate through indices. – duke_nukem Apr 15 '12 at 08:01
  • Well in this case you are supposed to use the dict syntax. However, depending on what you want to do, consider using a real template engine such as [jinja2](http://jinja.pocoo.org/). – ThiefMaster Apr 15 '12 at 08:02
  • Thanks for the template direction. – duke_nukem Apr 15 '12 at 08:04
  • Also, just adding, `dict` is not a function. `dict` is a type. –  Apr 15 '12 at 09:15
  • Well when used with `()` it's like a function. But I've replaced "function" with "callable" now... – ThiefMaster Apr 15 '12 at 09:33
1

You could pass locals():

a.format(**locals())

Of course, this has issues: you will have to pass everything in locals, and it can be difficult to understand the effect of renaming or removing a variable.

A better way would be:

a.format(**{k:v for k,v in locals() if k in ('year', 'month')})
# or; note that if you move the lambda expression elsewhere, you will get a different result
a.format(**(lambda ld = locals(): {k:ld[k] for k in ('year', 'month')})())

But this is not any more concise, unless you wrap it up with a function (which must of course take a dict parameter).

Marcin
  • 48,559
  • 18
  • 128
  • 201
0

As of Python 3.6, you can also use the new Formatted string literals (f-strings), which you can use with variables:

year = 2012
month = 'april'
location = 'q2dm1'
a = f"year: {year}, month: {month}, location: {location}"
print(a)

or a dictionary:

data = {'year': 2012, 'month': 'april', 'location': 'q2dm1'}
a = f"year: {data['year']}, month: {data['month']}, location: {data['location']}"
print(a)

Note the f prefix before the string literal.

PEP 498: Formatted string literals:

Formatted string literals are prefixed with 'f' and are similar to the format strings accepted by str.format(). They contain replacement fields surrounded by curly braces. The replacement fields are expressions, which are evaluated at run time, and then formatted using the format() protocol:

>>>
>>> name = "Fred"
>>> f"He said his name is {name}."
'He said his name is Fred.'
>>> width = 10
>>> precision = 4
>>> value = decimal.Decimal("12.34567")
>>> f"result: {value:{width}.{precision}}"  # nested fields
'result:      12.35'

...

quasoft
  • 5,291
  • 1
  • 33
  • 37