Dipende molto da quanto spesso pensi che l'eccezione sarà lanciata.
Entrambi gli approcci sono, a mio parere, ugualmente validi, almeno in termini di leggibilità e pitonicità. Ma se il 90% dei tuoi oggetti non ha l'attributo bar
noterai una differenza di prestazioni distinta tra i due approcci:
>>> import timeit
>>> def askforgiveness(foo=object()):
... try:
... x = foo.bar
... except AttributeError:
... pass
...
>>> def askpermission(foo=object()):
... if hasattr(foo, 'bar'):
... x = foo.bar
...
>>> timeit.timeit('testfunc()', 'from __main__ import askforgiveness as testfunc')
2.9459929466247559
>>> timeit.timeit('testfunc()', 'from __main__ import askpermission as testfunc')
1.0396890640258789
Ma se il 90% degli oggetti fanno hanno l'attributo, le tabelle sono state girate:
>>> class Foo(object):
... bar = None
...
>>> foo = Foo()
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askforgiveness as testfunc, foo')
0.31336188316345215
>>> timeit.timeit('testfunc(foo)', 'from __main__ import askpermission as testfunc, foo')
0.4864199161529541
Quindi, dal punto di vista delle prestazioni, devi scegliere l'approccio più adatto alle tue circostanze.
Alla fine, un uso strategico del % modulo ditimeit
potrebbe essere la cosa più pitonica puoi farlo.