Not really, no.
An exception can't be tested for ahead of time (well, it could if you can check the underlying reasons for it in advance, but let's assume you can't here). So you have to do it.
It will have to be nested somewhere under an if/else
construct and will end up looking much like your example.
To me at least, both answers given so far are quite a bit less clear in their intent than your code. I'd have to stop and think a bit about what the code execution flow is likely to do under various conditions and why the code was structured that way.
Don't repeat yourself is important - duplicated code is a code smell, often. But simplicity and obviousness has a virtue all of its own.
Zen of Python quotes:
Simple is better than complex.
Readability counts.
Errors should never pass silently.
Now, maybe you are not telling us the whole story and you have 20 branches of if-elif-elif-elif....else
. Or maybe it's not really b = func_2()
but 20 lines of duplicated code.
Post that code then, people can often replace big choices like that with dictionaries or maybe the duplicated code can be moved to another function.
But regarding your original question, I think the best is leave-as-is.
Now, I know yours is sample code, but there is one thing I would change:
try:
b = func_1()
except (<your expected exception>,):
b = func_2()
Having a bare except :
that then suppresses the exception is a massive swamp waiting to drag you down at the first opportunity in Python. It will bite you. Only do it trivial things like maybe a non-essential decorating an exception message in code - if that doesn't work - just skip adding the extra info.