2015-04-06 10 views
6

Sto cercando un modo più elegante di dichiarare un valore variabile in cui la funzione potrebbe restituire None e ci sono metodi concatenati che seguono la chiamata di funzione.Python: modo sicuro ed elegante per impostare una variabile dalla funzione che può restituire Nessuno

Nell'esempio che segue sto usando BeautifulSoup per passare un documento HTML e se l'elemento che sto cercando non viene trovato, la chiamata della funzione iniziale restituisce None. I metodi concatenati quindi interrompono il codice perché .string non è un metodo dell'oggetto None.

Quale tutto ha senso, ma mi chiedo se c'è un modo più pulito per scrivere queste dichiarazioni variabili che non si interromperanno su un valore None.

# I want to do something like this but it throws error if soup.find returns 
# none because .string is not a method of None. 
title = soup.find("h1", "article-title").string or "none" 


# This works but is both ugly and inefficient 
title = "none" if soup.find("h1", "article-title") is None else soup.find("h1", "article-title").string 

# So instead I'm using this which feels clunky as well 
title = soup.find("h1", "article-title") 
title = "none" if title is None else title.string 

Un modo migliore?

+1

Penso che in questo caso, il 'try ... ad eccezione di AttributeError' wrap è il metodo predefinito (o più semplice) per risolverlo, in particolare per un lungo set di metodi concatenati. – Evert

+1

È chiaramente alla ricerca di [operatore di navigazione sicuro] (http://docs.groovy-lang.org/latest/html/documentation/index.html#_safe_navigation_operator) come viene chiamato in Groovy. Non esiste un tale costrutto in Python. Ma sono d'accordo che potrebbe essere utile, specialmente quando hai una lunga catena di metodi che chiamano potenzialmente restituire Nessuno. @Esegui l'unico problema con la soluzione 'try ... except AttributeError ... 'non puoi distinguere tra un'eccezione generata da uno dei metodi concatenati che restituisce' None' e un'eccezione "reale" generata da un metodo chiamato (cioè : "nasconderà" AttributeError') –

risposta

1

È possibile utilizzare la funzione built-in getattr per fornire un valore di default nel caso in cui l'attributo desiderato non si trova all'interno di un determinato oggetto:

title = getattr(soup.find("h1", "article-title"), "string", "none") 

In alternativa, è possibile utilizzare un try statement:

try: 
    title = soup.find("h1", "article-title").string 
except AttributeError: 
    title = "none" 

Il primo metodo è più elegante secondo me.

+1

L'uso di 'getattr' è davvero una soluzione quando si accede agli attributi dei dati (come nell'esempio OP). Ma non risolve il problema più in generale quando si chiama _ "metodi concatenati" _ (come affermato anche dall'OP). O mi sono perso qualcosa? –

+0

Penso che userò try/eccetto che mentre è meno elegante non è chiaro quale sia lo scopo. E come afferma @SylvainLeroux, getattr non funzionerà con i metodi concatenati che sto usando anche per altri vars. Ma bello sapere anche su getattr. Grazie. – patnz

+0

@patns Bene, come probabilmente saprai, in Python le funzioni sono solo valori che puoi passare liberamente. Quindi, anche se si desidera eseguire una "chiamata di metodo concatenata" come 'obj.call1(). Call2(). Call3()', è ancora possibile farlo con 'getattr (getattr (getattr (obj," call1 ", dfn1)(), "call2", dfn2)(), "call3", dfn3)() '. Basta definire le funzioni "predefinite" e passarle in giro con il loro nome. – Shashank

4

mi piace la risposta di Shashank, ma questo potrebbe funzionare per voi pure:

class placeholder: 
    string = "none" 

title = (soup.find("h1", "article-title") or placeholder).string 
2

Questo comportamento di Beautiful Soup veramente mi infastidisce pure. Ecco la mia soluzione: http://soupy.readthedocs.org/en/latest/

Questo attenua sopra un sacco di casi limite in BeautifulSoup, che consente di scrivere query come

dom.find('h1').find('h2').find('a')['href'].orelse('not found').val() 

che restituisce quello che stai cercando se esiste, o 'non trovato' altrimenti .

La strategia generale in zuppa è quella di avvolgere i dati che ti interessano nelle classi di involucro sottile. Un semplice esempio di tale involucro:

class Scalar(object): 
    def __init__(self, val): 
     self._val = val 
    def __getattr__(self, key): 
     return Scalar(getattr(self._val, key, None)) 
    def __call__(self, *args, **kwargs): 
     return Scalar(self._val(*args, **kwargs)) 
    def __str__(self): 
     return 'Scalar(%s)' % self._val 


s = Scalar('hi there') 
s.upper() # Scalar('HI THERE') 
s.a.b.c.d # Scalar(None) 

Se si vuole essere di fantasia su di esso, la proprietà matematica che consente di catena in modo sicuro le cose per sempre è chiusura (vale a dire i metodi restituiscono istanze dello stesso tipo). Molti metodi di BeautifulSoup non hanno questa proprietà, che è ciò che indirizzi zuppa.

Problemi correlati