2014-07-26 18 views
5

Ho letto ultimamente un po 'tweets e la python documentation su hasattr e dice:Python hasattr vs getattr

hasattr (oggetto, nome)

gli argomenti sono un oggetto e una stringa. Il risultato è True se la stringa è il nome di >> uno degli attributi dell'oggetto, False se non. (Questo viene implementato chiamando getattr (object, name) e vedere se si solleva un AttributeError o no.)

C'è un motto in Python che dice che è più facile chiedere perdono che il permesso dove di solito sono d'accordo.

tentato di eseguire un test di performance in questo caso con una molto semplice codice python:

import timeit 
definition="""\ 
class A(object): 
    a = 1 
a = A() 
""" 

stm="""\ 
hasattr(a, 'a') 
""" 
print timeit.timeit(stmt=stm, setup=definition, number=10000000) 

stm="""\ 
getattr(a, 'a') 
""" 
print timeit.timeit(stmt=stm, setup=definition, number=10000000) 

Con i risultati:

$ python test.py 
hasattr(a, 'a') 
1.26515984535 

getattr(a, 'a') 
1.32518696785 

Ive provato anche cosa succede se l'attributo doesn Esiste e le differenze tra getattr e hasattr sono maggiori. Quindi quello che ho visto finora è che getattr è più lento di hasattr, ma nella documentazione dice che chiama getattr.

Ive cercato l'attuazione CPython di hasattr e getattr e sembra che sia chiamano la funzione successiva:

v = PyObject_GetAttr(v, name); 

ma c'è di più boilerplate in getattr che in hasattr che, probabilmente, lo rende più lento.

Qualcuno sa perché nella documentazione diciamo che hasattr chiama getattr e sembra incoraggiare gli utenti a utilizzare getattr invece di hasattr quando non è davvero dovuto alle prestazioni? È solo perché è più pitonico?

Forse sto facendo qualcosa di sbagliato nel mio test :)

Grazie,

Raúl

+3

La differenza qui non è la prestazione. Stai parlando di 5 nanosecondi. Perdi migliaia di volte usando semplicemente Python. La differenza qui è quale è corretta per ciò che devi fare. – cHao

+0

La differenza di prestazioni non era il punto. Non capivo perché il commento nella documentazione attestante che era stato implementato chiamando getattr era lì. – raulcumplido

+0

Forse a un certo punto ha chiamato 'getattr', e ora no, ma nessuno ha aggiornato i documenti ...? Francamente, però, questo è il tipo di dichiarazione che non appartiene comunque alla documentazione API. I documenti dovrebbero spiegare cosa succede, non come accade, a meno che il "come" sia una parte essenziale del "cosa". – cHao

risposta

10

La documentazione non incoraggia, la documentazione Uniti solo l'ovvio. Lo hasattr è implementato come tale e il lancio di un AttributeError da un getter di proprietà può far sembrare che l'attributo non esista. Questo è un dettaglio importante, ed è per questo che è esplicitamente indicato nella documentazione. Consideriamo per esempio questo codice:

class Spam(object): 
    sausages = False 

    @property 
    def eggs(self): 
     if self.sausages: 
      return 42 
     raise AttributeError("No eggs without sausages") 

    @property 
    def invalid(self): 
     return self.foobar 


spam = Spam() 
print(hasattr(Spam, 'eggs')) 

print(hasattr(spam, 'eggs')) 

spam.sausages = True 
print(hasattr(spam, 'eggs')) 

print(hasattr(spam, 'invalid')) 

Il risultato è

True 
False 
True 
False 

che è la classe Spam ha un descrittore di proprietà per eggs, ma dal momento che il getter solleva AttributeError se not self.sausages, quindi l'istanza di quella classe non "hasattr" eggs.

Oltre a ciò, utilizzare hasattr solo se si non è necessario il valore; se è necessario il valore, utilizzare getattr con 2 argomenti e rilevare l'eccezione o 3 argomenti, il terzo è un valore predefinito ragionevole.

I risultati usando getattr() (2.7.9):

>>> spam = Spam() 
>>> print(getattr(Spam, 'eggs')) 
<property object at 0x01E2A570> 
>>> print(getattr(spam, 'eggs')) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 7, in eggs 
AttributeError: No eggs without sausages 
>>> spam.sausages = True 
>>> print(getattr(spam, 'eggs')) 
42 
>>> print(getattr(spam, 'invalid')) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 10, in invalid 
AttributeError: 'Spam' object has no attribute 'foobar' 
>>> 
1

Sembra che hasattr ha un problema con la deglutizione eccezioni (almeno in Python 2.7), quindi probabilmente è meglio stare lontano da esso fino a quando è fisso .

Prendiamo, per esempio, the following code: