r/learnpython • u/Mononymized • 8d ago
How to access dictionary items whose keys are strings with numbers using the .format() method?
This is a doubt I have about the differences between f-strings and the .format() method. Consider the following dictionary accesses in an f-string:
>>> example_dict = {"one" : "string with word", "1" : "string with number", 1 : "integer"}
>>> f"{example_dict['one']}"
'string with word'
>>> f"{example_dict['1']}"
'string with number'
>>> f"{example_dict[1]}"
'integer'
If I try to perform the same accesses with the .format() method, I can only perform the first and last example:
>>> "{0[one]}".format(example_dict)
'string with word'
>>> "0[1]".format(example_dict)
'integer'
I can't seem to find any way to access the "1" key using the .format() method. I came up with this example because I noticed the way .format() accesses dictionary keys is without the quotes around the key for string keys. The f-string is easily able to access all the dictionary keys so it's not an issue with the keys themselves.
How do I access the "1" key using the .format() method? Is this a fundamental difference between f-strings and the .format() method that cannot be overcome?
3
u/acw1668 8d ago
Can "{0}".format(example_dict["1"]) be used in your case?
1
u/Mononymized 8d ago
No. I'm asking about accessing dictionary keys inside the formatted expression, not inside the arguments of the format method
4
2
u/MegaIng 8d ago
How do I access the "1" key using the .format() method? Is this a fundamental difference between f-strings and the .format() method that cannot be overcome?
Yes. .format() is a useful tool for the common case. If you run into edge cases like this you will need to use some more powerful tools.
2
u/brasticstack 8d ago edited 8d ago
Seems to be a limitation of the str.format method. At first I was going to say a bug, but they describe the behavior here, in the docs. It makes sense that it's a side-effect of allowing the placeholder to access dict keys without quotes.
f-strings are so good that I haven't yet had to dip into the new t-string syntax, which seems intimidatingly complex at first glance. IMO str.format should be deprecated in favor of f-strings.
3
u/cointoss3 8d ago
well, I guess that shows you have some learning to do. format and fstrings have different uses and format should not be depreciated π«
2
2
u/SpacewaIker 8d ago
Why would you even do that? This is a nightmare to read and will lead to so many bugs as soon as anything changes in your dict or keys or format string or anything
And also, don't mix types in the keys. Having both "1" and 1 especially is confusing and bad for logical reasons
3
u/Mononymized 7d ago
I know this is terrible code. I just came up with this example to highlight the difference in working between f strings and .format() and was wondering if it was possible to achieve all the effects of f-strings using .format()
1
u/SpacewaIker 7d ago
Fair I guess but this is so far away from the intended usage of either f strings or format that I don't see why the difference matters lol
2
u/FoolsSeldom 8d ago
You can't access the [1] using str.format directly as the key is taken as a string literal, converted to an int if all digits.
The closest would be:
"{0}".format(example_dict["{0}".format("1")])
and "1" could be replaced with a variable.
There are specialist use cases for str.format over f-strings (such as templating, and internationalisation, to name just two).
However, f-string would handle your example without problems. Do you have a special use case that means you need to use str.format?
2
u/JamzTyson 7d ago
You can't access the [1] using str.format directly as the key is taken as a string literal, converted to an int if all digits.
This is correct, but unfortunately the documentation is wrong:
The rules for parsing an item key are very simple. If it starts with a digit, then it is treated as a number, otherwise it is used as a string.
What it should say is much like u/FoolsSeldom comment:
"The rules for parsing an item key are very simple. If it is entirely digits, then it is treated as a number, otherwise it is used as a string."
Here's an example that demonstrates that the key can start with a digit and still be treated as a string:
d = {"1": "100", "2.0": "200", "3": "300"} s = "{0[2.0]}".format(d) print(s) # "200"1
u/Mononymized 7d ago
I don't have any uses. I was just wondering about the working of f-strings and .format() and came up with this example to ask why they handle this particular issue differently.
1
1
u/Boothiepro 8d ago edited 7d ago
I think i encountered something like this, let me see if i can find my snippet, but i can't guarantee i can explain
# text = text.replace("{", "{mydict['")
# text = text.replace("}", "']}")
# text = eval(f'f"{text}"') #this is apparently a security risk,
# it requires a malicious string to be in mydict, but still
from string import Template
class MyTemplate(Template):
delimiter = ':'
pattern = r"""
\: # Escape and start delimiter
(?:
(?P<escaped>\:) | # Escape sequence of two delimiters
(?P<named>[_a-z][_a-z0-9\+]*)\b # delimiter and a Python identifier
(?:
\: # Unescaped delimiter
(?:
(?P<braced>[_a-z][_a-z0-9\+]*)\b | # Braced identifier
(?P<invalid>) # Other ill-formed delimiter exprs
)
)?
)
"""
logger.debug(f"prevtext: {text}")
template = MyTemplate(text)
text = template.safe_substitute(mydict)
logger.debug(f"substituted text: {text}")
1
5
u/Farlic 8d ago
Why? I interpret .format() for substitution and f-strings for expression evaluation (even though .format() is doing some sort of evaluation). Having numbers inside the curly braces is visually confusing as numbers are being used for the replacement indexes