The official solution (Python 3 Docs) for strings in format mappings is to subclass the dict class and to define the magic-method __missing__(). This method is called whenever a key is missing, and what it returns is used for the string formatting instead:
class format_dict(dict):
def __missing__(self, key):
return "..."
d = format_dict({"foo": "name"})
print("My %(foo)s is %(bar)s" % d) # "My name is ..."
print("My {foo} is {bar}".format(**d)) # "My name is ..."
Edit: the second print() works in Python 3.5.3, but it does not in e.g. 3.7.2: KeyError: 'bar' is raised and I couldn't find a way to catch it.
After some experiments, I found a difference in Python's behavior. In v3.5.3, the calls are __getitem__(self, "foo") which succeeds and __getitem__(self, "bar") which can not find the key "bar", therefore it calls __missing__(self, "bar") to handle the missing key without throwing a KeyError. In v3.7.2, __getattribute__(self, "keys") is called internally. The built-in keys() method is used to return an iterator over the keys, which yields "foo", __getitem__("foo") succeeds, then the iterator is exhausted. For {bar} from the format string there is no key "bar". __getitem__() and hence __missing_() are not called to handle the situation. Instead, the KeyError is thrown. I don't know how one could catch it, if at all.
In Python 3.2+ you should use format_map() instead (also see Python Bug Tracker - Issue 6081):
from collections import defaultdict
d = defaultdict(lambda: "...")
d.update({"foo": "name"})
print("My {foo} is {bar}".format_map(d)) # "My name is ..."
If you want to keep the placeholders, you can do:
class Default(dict):
def __missing__(self, key):
return key.join("{}")
d = Default({"foo": "name"})
print("My {foo} is {bar}".format_map(d)) # "My name is {bar}"
As you can see, format_map() does call __missing__().
The following appears to be the most compatible solution as it also works in older Python versions including 2.x (I tested v2.7.15):
class Default(dict):
def __missing__(self, key):
return key.join("{}")
d = Default({"foo": "name"})
import string
print(string.Formatter().vformat("My {foo} is {bar}", (), d)) # "My name is {bar}"
To keep placeholders as-is including the format spec (e.g. {bar:<15}) the Formatter needs to be subclassed:
import string
class Unformatted:
def __init__(self, key):
self.key = key
def __format__(self, format_spec):
return "{{{}{}}}".format(self.key, ":" + format_spec if format_spec else "")
class Formatter(string.Formatter):
def get_value(self, key, args, kwargs):
if isinstance(key, int):
try:
return args[key]
except IndexError:
return Unformatted(key)
else:
try:
return kwargs[key]
except KeyError:
return Unformatted(key)
f = Formatter()
s1 = f.vformat("My {0} {1} {foo:<10} is {bar:<15}!", ["real"], {"foo": "name"})
s2 = f.vformat(s1, [None, "actual"], {"bar":"Geraldine"})
print(s1) # "My real {1} name is {bar:<15}!"
print(s2) # "My real actual name is Geraldine !"
Note that the placeholder indices are not changed ({1} remains in the string without a {0}), and in order to substitute {1} you need to pass an array with any odd first element and what you want to substitute the remaining placeholder with as second element (e.g. [None, "actual"]).
You can also call the format() method with positional and named arguments:
s1 = f.format("My {0} {1} {foo:<10} is {bar:<15}!", "real", foo="name")
s2 = f.format(s1, None, "actual", bar="Geraldine")
Answer from CodeManX on Stack OverflowThe official solution (Python 3 Docs) for strings in format mappings is to subclass the dict class and to define the magic-method __missing__(). This method is called whenever a key is missing, and what it returns is used for the string formatting instead:
class format_dict(dict):
def __missing__(self, key):
return "..."
d = format_dict({"foo": "name"})
print("My %(foo)s is %(bar)s" % d) # "My name is ..."
print("My {foo} is {bar}".format(**d)) # "My name is ..."
Edit: the second print() works in Python 3.5.3, but it does not in e.g. 3.7.2: KeyError: 'bar' is raised and I couldn't find a way to catch it.
After some experiments, I found a difference in Python's behavior. In v3.5.3, the calls are __getitem__(self, "foo") which succeeds and __getitem__(self, "bar") which can not find the key "bar", therefore it calls __missing__(self, "bar") to handle the missing key without throwing a KeyError. In v3.7.2, __getattribute__(self, "keys") is called internally. The built-in keys() method is used to return an iterator over the keys, which yields "foo", __getitem__("foo") succeeds, then the iterator is exhausted. For {bar} from the format string there is no key "bar". __getitem__() and hence __missing_() are not called to handle the situation. Instead, the KeyError is thrown. I don't know how one could catch it, if at all.
In Python 3.2+ you should use format_map() instead (also see Python Bug Tracker - Issue 6081):
from collections import defaultdict
d = defaultdict(lambda: "...")
d.update({"foo": "name"})
print("My {foo} is {bar}".format_map(d)) # "My name is ..."
If you want to keep the placeholders, you can do:
class Default(dict):
def __missing__(self, key):
return key.join("{}")
d = Default({"foo": "name"})
print("My {foo} is {bar}".format_map(d)) # "My name is {bar}"
As you can see, format_map() does call __missing__().
The following appears to be the most compatible solution as it also works in older Python versions including 2.x (I tested v2.7.15):
class Default(dict):
def __missing__(self, key):
return key.join("{}")
d = Default({"foo": "name"})
import string
print(string.Formatter().vformat("My {foo} is {bar}", (), d)) # "My name is {bar}"
To keep placeholders as-is including the format spec (e.g. {bar:<15}) the Formatter needs to be subclassed:
import string
class Unformatted:
def __init__(self, key):
self.key = key
def __format__(self, format_spec):
return "{{{}{}}}".format(self.key, ":" + format_spec if format_spec else "")
class Formatter(string.Formatter):
def get_value(self, key, args, kwargs):
if isinstance(key, int):
try:
return args[key]
except IndexError:
return Unformatted(key)
else:
try:
return kwargs[key]
except KeyError:
return Unformatted(key)
f = Formatter()
s1 = f.vformat("My {0} {1} {foo:<10} is {bar:<15}!", ["real"], {"foo": "name"})
s2 = f.vformat(s1, [None, "actual"], {"bar":"Geraldine"})
print(s1) # "My real {1} name is {bar:<15}!"
print(s2) # "My real actual name is Geraldine !"
Note that the placeholder indices are not changed ({1} remains in the string without a {0}), and in order to substitute {1} you need to pass an array with any odd first element and what you want to substitute the remaining placeholder with as second element (e.g. [None, "actual"]).
You can also call the format() method with positional and named arguments:
s1 = f.format("My {0} {1} {foo:<10} is {bar:<15}!", "real", foo="name")
s2 = f.format(s1, None, "actual", bar="Geraldine")
str.format() doesn't expect a mapping object. Try this:
from collections import defaultdict
d = defaultdict(str)
d['error2'] = "success"
s = "i am an {0[error]} example string {0[error2]}"
print s.format(d)
You make a defaultdict with a str() factory that returns "". Then you make one key for the defaultdict. In the format string, you access keys of the first object passed. This has the advantage of allowing you to pass other keys and values, as long as your defaultdict is the first argument to format().
Also, see http://bugs.python.org/issue6081
If you are using Python 3.2+, use can use str.format_map().
For bond, bond:
from collections import defaultdict
'{bond}, {james} {bond}'.format_map(defaultdict(str, bond='bond'))
Result:
'bond, bond'
For bond, {james} bond:
class SafeDict(dict):
def __missing__(self, key):
return '{' + key + '}'
'{bond}, {james} {bond}'.format_map(SafeDict(bond='bond'))
Result:
'bond, {james} bond'
In Python 2.6/2.7
For bond, bond:
from collections import defaultdict
import string
string.Formatter().vformat('{bond}, {james} {bond}', (), defaultdict(str, bond='bond'))
Result:
'bond, bond'
For bond, {james} bond:
from collections import defaultdict
import string
class SafeDict(dict):
def __missing__(self, key):
return '{' + key + '}'
string.Formatter().vformat('{bond}, {james} {bond}', (), SafeDict(bond='bond'))
Result:
'bond, {james} bond'
You could use a template string with the safe_substitute method.
from string import Template
tpl = Template('$bond, $james $bond')
action = tpl.safe_substitute({'bond': 'bond'})
KeyError in string format when curlies { } present
Python format throws KeyError - Stack Overflow
Format function: Fill missing keys with empty string
python - Ignore KeyError and continue program - Stack Overflow
Videos
Hey guys,
I'm working in a very specific enviroment and I have a scheme file where I replace keys with the format function. The file might look like this:
1: {code1}
2: {code2}
3: {code3}I want that other persons can change the format file and add {code} tags. My program uses this file and replaces the values, but the amount of {code} blocks varies.
So at the moment I generate a dict with the code tags, and replace them in that string (which got loaded from the file)
code_tags = {"code1": "ABC", "code2": "DEF"}
file_content.format(**code_tags)A problem occurs when there are more keys in the scheme file than keys generated from my program (KeyError). How can I put in an empty string for tags which got not specified in code_tags?
I thought about crawling the file and find every tag, compare, and add missing to the dict with and emptry string. But that's kinda hask-ish and maybe not the cleanest style.
How would you solve the problem?
Why not:
for code in portfolio_list:
try:
share_name, share_value = EXCHANGE_DATA[code]
print('{:<6} {:<20} {:>8.2f}'.format(code, share_name, share_value)
except KeyError:
continue
OR check dict.get method:
for code in portfolio_list:
res = EXCHANGE_DATA.get(code, None)
if res:
print('{:<6} {:<20} {:>8.2f}'.format(code, *res)
And as @RedBaron mentioned:
for code in portfolio_list:
if code in EXCHANGE_DATA:
print('{:<6} {:<20} {:>8.2f}'.format(code, *EXCHANGE_DATA[code])
catch the exception in the loop
for code in portfolio_list:
try:
share_name, share_value = EXCHANGE_DATA[code]
print('{:<6} {:<20} {:>8.2f}'.format(code, share_name, share_value)
except KeyError:
pass
Edit: The more pythonic way would be to test if the dict has the element first
for code in portfolio_list:
if code in EXCHANGE_DATA:
share_name, share_value = EXCHANGE_DATA[code]
print('{:<6} {:<20} {:>8.2f}'.format(code, share_name, share_value)
You can use Template and safe_substitute for that:
from string import Template
practise_dict = {"Additional_Details":"blah blah blah blah", "Title": "Mr", "Surname": "Smith", "URL": "/test/tester"}
msg = """From: John Smith <[email protected]>
To: $Title $Surname <[email protected]>
MIME-Version: 1.0
Content-type: text/html
Subject: New Website Enquiry
This is an e-mail message to be sent in HTML format
$Additional_Details
$Date
<b>This is HTML message.</b>
<h1>This is headline.</h1>
`"""
s = Template(msg).safe_substitute(**practise_dict)
print(s)
OUTPUT
From: John Smith <[email protected]>
To: Mr Smith <[email protected]>
MIME-Version: 1.0
Content-type: text/html
Subject: New Website Enquiry
This is an e-mail message to be sent in HTML format
blah blah blah blah
$Date
<b>This is HTML message.</b>
<h1>This is headline.</h1>
I don't think there's a way to tell format that some substitutions are optional. However, here are two workarounds.
If there's only one missing field, try something like:
msg = """...""".format(Date=practise_dict.pop("Date", ""), # handle optional field
**practise_dict) # pass the rest as before
However, note that this modifies practise_dict, and will be awkward if there are several values to deal with.
If there may be multiple fields missing, or you don't want to modify the dictionary, the following would probably be preferable:
defaults = {"Date": "", "Additional Details": ""} # define optional fields
defaults.update(practise_dict) # override user data where available
msg = """...""".format(**defaults)
Both approaches will still fail for keys you haven't explicitly provided an alternative for, which allows you to make some fields mandatory.
Hey I've been working on a project and I came across a error of: KeyError: '\n type'
I've been looking online for solutions but I've only found KeyError: '\n' (without the 'type' and none of the solutions work). What I'm trying to do is take a list and get a chart back from quickchart.io. Its worked fine without using variable data, but now that im trying to it isn't working.
My code:
import quickchart from QuickChart
coins = ['1','2','3','4','5']
bags = ['1','2','3','4','5']
cash = ['1','2','3','4','5']
qc = QuickChart()
qc.width = 1000
qc.height = 600
qc.version = "2"
qc.config = '''{
type: 'line',
data: {
labels: ['24hrs', '18hrs', '12hrs', '6hrs', 'now'],
datasets: [
{
label: 'coins',
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, .5)',
data: {},
},
{
label: 'bags',
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, .5)',
data: {},
},
{
label: 'cash',
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, .5)',
data: {},
},
],
},
options: {
responsive: true,
title: {
display: true,
text: 'Stock Market',
},
tooltips: {
mode: 'index',
},
hover: {
mode: 'index',
},
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Time Since',
},
},
],
yAxes: [
{
stacked: true,
scaleLabel: {
display: true,
labelString: 'Value',
},
},
],
},
},
}'''.format(coins, bags, cash) #This is the line causing the error according to my consolethe indents in the stringed-dict may be stuffed up from copy and paste, but hopefully that doesn't cause an issue for you.
I've also tried doing :
...
data: ['{}','{}','{}','{}','{}']
...
}'''.format(coins[0], coins[1]) #and so onwhich has the same error
In case you need it, is the documentation for quickchart (https://quickchart.io/documentation/)
let me know if you need other information, I'd be happy to give it to you.
I have a text file that gets read in to the program, and it also contains a JSON file.
An example of what I'm talking about is this:
Hello, {}. Welcome to /r/{}. This is a JSON file.
---
{
"key": "value"
}
That is stored in a text file that we will call example.txt. I read the file in as such:
def read_text_file(file_name):
with open(file_name, "r") as file:
return file.read()
my_string = read_text_file("example.txt")
formatted_string = my_string.format(username, subreddit)
However, because the dictionary also has a {} in it, .format() seems to expect a third argument with .format(). Is there a way to make it ignore the third one so that I don't get a KeyError exception?
Yes, You can replace
print("Subnet ID: ", subnet['Tags'])
with
print("Subnet ID: ", subnet.get('Tags', ''))
Using get with allow you to define a default value in case Tags doesn't exist
Catch the KeyError exception:
try:
print("Tags: ", vpctags['Tags'])
except KeyError:
print("Tags: None")
If the Tags key doesn't exist, it will instead print "None".
The problem is that those { and } characters you have there don't specify a key for formatting. You need to double them up, so change your code to:
addr_list_formatted.append("""
"{0}"
{{
"gamedir" "str"
"address" "{1}"
}}
""".format(addr_list_idx, addr))
Using str.format() to format JSON strings is not ideal because you would have to escape the curly braces, as the accepted answer notes.
While this method may be suitable for small JSON templates, it could make the template difficult to read if there are many curly braces that require escaping.
A better alternative could be string.Template:
from string import Template
addr_list = ["address 1, country 1", "address 2, country 2"]
addr_list_formatted = []
addr_list_idx = 0
template = Template("""
"${index}"
{
"gamedir" "str"
"address" "${address}"
}
""")
for addr in addr_list:
addr_list_idx = addr_list_idx + 1
formatted = template.substitute(index=addr_list_idx, address=addr)
addr_list_formatted.append(formatted)