Советую вам посмотреть в сторону декораторов. Полезно будет как для решения задачи, так и для общего ознакомления с синтаксическими возможностями языка.
Дам пример самого простого декоратора, единственная функция которого "тихо" логгировать любой exception:
import logging
LOG = logging.getLogger(__name__)
def handle_silently(function):
def wrapped(*args, **kwargs):
result = None
try:
result = function(*args, **kwargs)
except Exception as e:
LOG.error(
'{}(*{}, **{}) failed with exception {}'.format(
function.__name__, repr(args), repr(kwargs), repr(e)
)
)
return result
return wrapped
@handle_silently
def some_function(test):
return test / 0
Конструкция @handle_silently заменит вашу функцию some_function на функцию wrapped.
Механика работы wrapped является, скорее, антипаттерном, поскольку замалчивать exception совсем и тем более отдавать вместо него None - это гарантированный способ выстрелить себе в ногу. Это просто пример, в боевом проекте я бы посоветовал ререйзить (LOG.error(...); raise) ошибку в декораторе и обрабатывать несредственно в месте вызова функции (логгирование, естественно, можно и оставить).
Для затравки и для демонстрации того, как это может быть полезно - декотораторы могут быть параметрическими (достигается это путём создания фабрики декораторов):
def decorators_fabric(handler_function):
def decorator(function):
def wrapped(*args, **kwargs):
handler_function(function.__name__, repr(args), repr(kwargs))
return function(*args, **kwargs)
return wrapped
return decorator
@decorators_fabric(print)
def my_test_function(arg1, arg2):
return arg1 * arg2