2011-03-11 13 views
63

Sto scrivendo un algoritmo di ricerca spaziale AI, e ho una classe generica che può essere utilizzata per implementare rapidamente un algoritmo di ricerca. Una sottoclasse definirà le operazioni necessarie e l'algoritmo farà il resto.Qual è il modo più veloce per verificare se una classe ha una funzione definita?

Qui è dove vengo bloccato: Voglio evitare rigenerare lo stato genitore più e più volte, in modo da avere la seguente funzione, che restituisce le operazioni che possono essere applicate legalmente a qualsiasi stato:

def get_operations(self, include_parent=True): 
    ops = self._get_operations() 
    if not include_parent and self.path.parent_op: 
     try: 
      parent_inverse = self.invert_op(self.path.parent_op) 
      ops.remove(parent_inverse) 
     except NotImplementedError: 
      pass 
    return ops 

E la funzione invert_op si attiva di default.

Esiste un modo più rapido per verificare se la funzione non è definita rispetto al rilevamento di un'eccezione?

Stavo pensando qualcosa sulla linea di controllo per presente in dir, ma quello non sembra giusto. hasattr è implementato chiamando getattr e controllando se si alza, che non è quello che voglio.

+0

qualcosa suona rotto, ma io non riesco a mettere il dito su di esso ... –

+6

* "hasattr è implementato chiamando getattr e controllando se si alza, che non è quello che voglio. "* Perché no? Perché ti importa cosa fa l'implementazione? – detly

+3

'has_op = lambda obj, op: callable (getattr (obj, op, None))' – samplebias

risposta

106

Sì, utilizzare getattr() per ottenere l'attributo, e callable() per verificare che è un metodo:

invert_op = getattr(self, "invert_op", None) 
if callable(invert_op): 
    invert_op(self.path.parent_op) 

Nota che getattr() tiri normalmente eccezione quando l'attributo non esiste. Tuttavia, se si specifica un valore predefinito (None, in questo caso), verrà restituito.

+2

Si noti inoltre che l'implementazione di 'getattr' in questo caso cattura un'eccezione silenziosa e restituisce il valore predefinito, proprio come fa' hasattr', contro cui l'OP era per qualche motivo contrario. – Santa

+2

Cosa succede se la funzione non è in quella classe, ma nella classe genitore ?. In questo caso ottengo una Vero, anche quando i bambini non implementano mai quella funzione (usando hasattr) – darkgaze

17

Esiste un modo più rapido per verificare se la funzione non è definita rispetto al rilevamento di un'eccezione?

Perché sei contrario? Nella maggior parte dei casi Python, è meglio chiedere perdono che il permesso. ;-)

hasattr è implementato chiamando getattr e controllando se si alza, che non è quello che voglio.

Ancora, perché è quello? Quanto segue è abbastanza Pythonic:

try: 
     invert_op = self.invert_op 
    except AttributeError: 
     pass 
    else: 
     parent_inverse = invert_op(self.path.parent_op) 
     ops.remove(parent_inverse) 

Oppure,

# if you supply the optional `default` parameter, no exception is thrown 
    invert_op = getattr(self, 'invert_op', None) 
    if invert_op is not None: 
     parent_inverse = invert_op(self.path.parent_op) 
     ops.remove(parent_inverse) 

Si noti, tuttavia, che getattr(obj, attr, default) è sostanzialmente attuata con la cattura un'eccezione, anche. Non c'è niente di sbagliato in Python land!

2

Come in Python, se ci provi abbastanza, puoi entrare nel coraggio e fare qualcosa di veramente brutto. Ora, ecco la parte brutta:

def invert_op(self, op): 
    raise NotImplementedError 

def is_invert_op_implemented(self): 
    # Only works in CPython 2.x of course 
    return self.invert_op.__code__.co_code == 't\x00\x00\x82\x01\x00d\x00\x00S' 

Si prega di farci un favore, basta continuare a fare quello che hai nella tua domanda e NON mai usare questo se non si è nella squadra di PyPy hacking in l'interprete Python . Quello che hai lassù è Pythonic, quello che ho qui è puro EVIL.

+0

Questo sarà vero se il metodo solleva qualsiasi eccezione. Dovresti anche controllare se 'co_names' è uguale a' ('NotImplementedError',) '. Non sono sicuro se ciò lo rende più o meno malvagio, comunque. – kindall

3

Mi piace la risposta di Nathan Ostgard e l'ho votato. Ma un altro modo per risolvere il problema potrebbe essere quello di utilizzare un decoratore memoizing, che memorizzerebbe in cache il risultato della chiamata di funzione.Quindi puoi andare avanti e avere una funzione costosa che calcola qualcosa, ma poi quando la chiami ripetutamente le chiamate successive sono veloci; la versione memoizzata della funzione cerca gli argomenti in un dict, trova il risultato nel dict da quando la funzione effettiva ha calcolato il risultato e restituisce immediatamente il risultato.

Ecco una ricetta per un decoratore memoizing chiamato "lru_cache" di Raymond Hettinger. Una versione di questo è ora standard nel modulo functools in Python 3.2.

http://code.activestate.com/recipes/498245-lru-and-lfu-cache-decorators/

http://docs.python.org/release/3.2/library/functools.html

18

Funziona sia Python 2 e Python 3

hasattr(connection, 'invert_opt') 

hasattr rendimenti True se oggetto di connessione ha una funzione invert_opt definita. Ecco la documentazione per voi a pascolare

https://docs.python.org/2/library/functions.html#hasattr https://docs.python.org/3/library/functions.html#hasattr

+3

Sebbene il codice sia apprezzato, dovrebbe sempre avere una spiegazione di accompagnamento. Questo non deve essere lungo ma è previsto. – peterh

+0

bene, puoi indicare un articolo anche se non sarebbe male :) –

+0

Questo restituisce anche True se la connessione ha un attributo 'connection.invert_opt = 'foo''. –

3

Le risposte controllare qui se una stringa è il nome di un attributo dell'oggetto. È necessario un ulteriore passo (usando callable) per verificare se l'attributo è un metodo.

Quindi si riduce a: qual è il modo più veloce per verificare se un oggetto obj ha un attributo attrib. La risposta è

'attrib' in obj.__dict__ 

Questo perché un dict hash le sue chiavi in ​​modo da controllare per l'esistenza della chiave è veloce.

Vedere i confronti temporali di seguito.

>>> class SomeClass(): 
...   pass 
... 
>>> obj = SomeClass() 
>>> 
>>> getattr(obj, "invert_op", None) 
>>> 
>>> %timeit getattr(obj, "invert_op", None) 
1000000 loops, best of 3: 723 ns per loop 
>>> %timeit hasattr(obj, "invert_op") 
The slowest run took 4.60 times longer than the fastest. This could mean that an intermediate result is being cached. 
1000000 loops, best of 3: 674 ns per loop 
>>> %timeit "invert_op" in obj.__dict__ 
The slowest run took 12.19 times longer than the fastest. This could mean that an intermediate result is being cached. 
10000000 loops, best of 3: 176 ns per loop 
0

Durante il controllo per gli attributi in immobili __dict__ è veramente veloce, non è possibile utilizzare questo per i metodi, dal momento che non appaiono in __dict__ hash. Si potrebbe tuttavia ricorrere a soluzione hacker nella tua classe, se la prestazione è che critica:

class Test(): 
    def __init__(): 
     # redefine your method as attribute 
     self.custom_method = self.custom_method 

    def custom_method(self): 
     pass 

quindi controllare Metodo:

t = Test() 
'custom_method' in t.__dict__ 

Tempo confronto con getattr:

>>%timeit 'custom_method' in t.__dict__ 
55.9 ns ± 0.626 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 

>>%timeit getattr(t, 'custom_method', None) 
116 ns ± 0.765 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 
Non

che sto incoraggiando questo approccio, ma sembra funzionare.

[EDIT] incremento delle prestazioni è ancora più elevato quando il nome metodo non è in data classe:

>>%timeit 'rubbish' in t.__dict__ 
65.5 ns ± 11 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each) 

>>%timeit getattr(t, 'rubbish', None) 
385 ns ± 12.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 
Problemi correlati