Python Perdono vs. Permesso e Duck Typing


44

In Python, ho spesso sentito che è meglio "chiedere perdono" (rilevazione delle eccezioni) invece di "chiedere l'autorizzazione" (controllo del tipo / condizione). Per quanto riguarda l'applicazione della tipizzazione anatra in Python, è questo

try:
    x = foo.bar
except AttributeError:
    pass
else:
    do(x)

meglio o peggio di

if hasattr(foo, "bar"):
    do(foo.bar)
else:
    pass

in termini di prestazioni, leggibilità, "pythonic" o qualche altro fattore importante?


17
c'è una terza opzione, non fare nulla e trattare qualsiasi foo senza barra come un bug
jk.

Ricordo di aver sentito che hasattrè implementato con quel preciso tentativo / cattura internamente. Non sono sicuro che sia vero ... ( getattr
Agirebbe

@Izkata: L' implementazione dihasattr usa l'equivalente C-API di getattr(return in Truecaso di successo, in Falsecaso contrario), ma la gestione delle eccezioni in C è molto più veloce.
Martijn Pieters,

2
Ho accettato la risposta di Martijn, ma vorrei aggiungere che se stai cercando di impostare un attributo, dovresti assolutamente considerare l'utilizzo di try / catch perché potrebbe essere una proprietà senza setter, nel qual caso hasattr sarà vero , ma genererà comunque AttributeError.
darkfeline,

Risposte:


60

Dipende davvero dalla frequenza con cui pensi che verrà generata l'eccezione.

Entrambi gli approcci sono, secondo me, ugualmente validi, almeno in termini di leggibilità e sensibilità pitonica. Ma se il 90% dei tuoi oggetti non ha l'attributo barnoterai 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 non hanno l'attributo, i tavoli sono stati trasformati:

>>> 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, è necessario scegliere l'approccio più adatto alle proprie circostanze.

Alla fine, un uso strategico del timeitmodulo potrebbe essere la cosa più Pythonic che puoi fare.


1
Alcune settimane fa ho posto questa domanda: programmers.stackexchange.com/questions/161798/… Lì ho chiesto se nelle lingue tipicamente vaghe dovevi lavorare di più facendo controlli sui tipi, e sono stato bombardato da persone che dicevano che non lo avevi fatto. So che vedo che hai.
Tulains Córdova,

@ user1598390: quando si definisce un'API che prevede una combinazione omogenea di tipi, è necessario eseguire alcuni test. Il più delle volte no. Questa è un'area specifica da cui non puoi derivare regole sui paradigmi nel loro insieme, temo.
Martijn Pieters,

Bene, qualsiasi serio sviluppo del sistema comporta la definizione di un'API. Quindi immagino che i linguaggi rigorosi di tipo siano i migliori per questo perché devi programmare meno poiché il compilatore controlla i tipi per te in fase di compilazione.
Tulains Córdova,

1
@GarethRees: stabilisce uno schema per la seconda metà della risposta in cui passo un argomento alla funzione sotto test.
Martijn Pieters,

1
Si noti che per hasattr, in realtà fa l'equivalente C-api di un try-tranne comunque sotto il cofano, poiché risulta l'unico modo generale per determinare se un oggetto ha un attributo in Python è provare ad accedervi.
user2357112 supporta Monica il

11

In Python spesso ottieni prestazioni migliori facendo le cose in modo Python. Con altre lingue, l'uso delle eccezioni per il controllo del flusso è generalmente considerato un'idea terribile perché le eccezioni impongono in genere un sovraccarico straordinario. Ma poiché questa tecnica è esplicitamente approvata in Python, l'interprete è ottimizzato per questo tipo di codice.

Come per tutte le domande sulle prestazioni, l'unico modo per essere certi è creare un profilo del codice. Scrivi entrambe le versioni e vedi quale corre più veloce. Sebbene nella mia esperienza, il "modo Python" è in genere il modo più veloce.


3

La performance, credo, è una preoccupazione secondaria. Se si presenta, un profiler ti aiuterebbe a concentrarti sui veri colli di bottiglia, che potrebbero essere o meno il modo in cui tratti i possibili argomenti illegali.

La leggibilità e la semplicità, d'altra parte, sono sempre una delle preoccupazioni principali. Non ci sono regole rigide qui, basta usare il tuo giudizio.

Questo è un problema universale, ma le convenzioni specifiche per l'ambiente o la lingua sono rilevanti. Ad esempio, in Python di solito va bene usare semplicemente l'attributo che ci si aspetta e lasciare che un possibile AttributeError raggiunga il chiamante.


-1

In termini di correttezza , penso che la gestione delle eccezioni sia la strada da percorrere (a volte uso l'approccio hasattr (), però). Il problema di base quando si fa affidamento su hasattr () è che trasforma le violazioni dei contratti di codice in errori silenziosi (questo è un grosso problema in JavaScript, che non genera proprietà inesistenti).


3
La tua risposta non sembra aggiungere molto oltre ciò che è già stato affermato da altri. A differenza di altri siti, i programmatori si aspettano risposte che spieghino il perché della risposta. Si tocca un buon punto con il problema del fallimento silenzioso, ma non si deve dare giustizia ad esso. La lettura di quanto segue può essere d'aiuto: programmers.stackexchange.com/help/how-to-answer

L'ultima risposta che ho fatto è stata criticata per essere troppo ampia. Ho pensato di provare breve e conciso.
Joe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.