Question: I want to silently ignore KeyErrors and instead replace them with placeholders if values are not found. For example: class Name: def __init__(self, name): self.name = name self.capitalized = name.capitalize() def __str__(self): return self.name "hello, {name}!".format(name=Name("bob")) # hello, bob! "greetings, {name.capitalized}!".format(name=Name("bob")) # greetings, Bob! # but, if no name kwarg is given... "hello, {name}!".format(age=34) # hello, {name}! "greetings, {name.capitalized}!".format(age=34) # greetings, {name.capitalized}!  My goal with this is that I'm trying to create a custom localization package for personal projects (I couldn't find existing ones that did everything I wanted to). Messages would be user-customizable, but I want users to have a flawless experience, so for example, if they make a typo and insert {nmae} instead of {name}, I don't want users to have to deal with errors, but I want to instead signal to them that they made a typo by giving them the placeholder value. I found several solutions on stackoverflow, but none of them can handle attributes. My first solution was this: class Default(dict): """A dictionary that returns the key itself wrapped in curly braces if the key is not found.""" def __missing__(self, key: str) -> str: return f"{{{key}}}"  But this results in an error when trying to use it with attributes: AttributeError: 'str' object has no attribute 'capitalized', it does print "hello, {name}!" with no issues. Same goes for my second solution using string.Formatter: class CustomFormatter(string.Formatter): def get_value(self, key, args, kwargs): try: value = super().get_value(key, args, kwargs) except KeyError: value = f'{{{key}}}' except AttributeError: value = f'{{{key}}}' return value formatter.format("hello, {name}!", name=Name("bob")) # hello, bob! formatter.format("greetings, {name.capitalized}!", name=Name("bob")) # greetings, Bob! formatter.format("hello, {name}!", age=42) # hello, {name}! formatter.format("greetings, {name.capitalized}!", age=42) # AttributeError: 'str' object has no attribute 'capitalized'  So how could I achieve something like this? "hello, {name}!".format(name=Name("bob")) # hello, bob! "greetings, {name.capitalized}!".format(name=Name("bob")) # greetings, Bob! # but, if no name kwarg is given... "hello, {name}!".format(age=34) # hello, {name}! "greetings, {name.capitalized}!".format(age=34) # greetings, {name.capitalized}! 

Answer: TL;DR The best solution is to override get_field instead of get_value in CustomFormatter: class CustomFormatter(string.Formatter): def get_field(self, field_name, args, kwargs): try: return super().get_field(field_name, args, kwargs) except (AttributeError, KeyError): return f"{{{field_name}}}", None  Kuddos to @blhsing for suggesting this solution. Details The issue is that the AttributeError gets raised when formatter.get_field() is called, not in get_value(), so you also need to override get_field(). By adding this function to your CustomFormatter class, I was able to get the behaviour you want with {name.capitalized} shown when you pass name="bob" or name=34 instead of name=Name("bob"):  def get_field(self, field_name, args, kwargs): try: return super().get_field(field_name, args, kwargs) except AttributeError: return f"{{{field_name}}}", None  The return value is a tuple, to respect get_field's return value: a tuple with the result, and the key used. In action >>> formatter = CustomFormatter() >>> formatter.format("greetings, {name.capitalized}!", name="bob") 'greetings, {name.capitalized}!' >>> formatter.format("greetings, {name.capitalized}!", name=34) 'greetings, {name.capitalized}!' >>> formatter.format("greetings, {name.capitalized}!", name=Name("bob")) 'greetings, Bob!' >>> formatter.format("{name.capitalized}, you are {age} years old.", name=Name("bob")) 'Bob, you are {age} years old.'  Tracing the code for deeper understanding When I added some debugging print statements, namely: class CustomFormatter(string.Formatter): def get_value(self, key, args, kwargs): print(f"get_value({key=}, {args=}, {kwargs=}") ... def get_field(self, field_name, args, kwargs): print(f"get_field({field_name=}, {args=}, {kwargs=}") ...  I could see this log when using name=Name("bob"): get_field(field_name='name.capitalized', args=(), kwargs={'name': <__main__.Name object at 0x000002818AA8E8D0>} get_value(key='name', args=(), kwargs={'name': <__main__.Name object at 0x000002818AA8E8D0>}  and this log with for age=34 and leaving out name: get_field(field_name='name', args=(), kwargs={'age': 34} get_value(key='name', args=(), kwargs={'age': 34}  so you see it's your overriden get_value that handles the wrong key, and my overriden get_field that handles the missing attribute. Making the code more concise As @blhsing pointed out, if you also catch the KeyError in get_field, then you don't need to override get_value at all, leading to the final solution in the TL;DR above.