2009-04-20 16 views
14

In Perl, è spesso piacevole poter assegnare un oggetto, ma specificare un valore di fallback se la variabile assegnata da è 'undef'. Per esempio:Qual è il modo più Pythonic per fornire un valore di fallback in un compito?

my $x = undef; 
my $y = 2; 
my $a = $x || $y; 

Dopo questo,

$a == 2 

C'è un modo conciso per raggiungere questo obiettivo in Python se il valore x è Nessuno, o semplicemente di una full-on ...

if x is not None 
    a = x 
else 
    a = y 

... essere il modo più Pythonic per raggiungere questo obiettivo?

EDIT: Mi scuso, come è stato sottolineato da diversi commentatori, non stavo parlando del valore non definito, ma 'undef' in Perl, che non è proprio la stessa cosa. Ma la domanda come originariamente formulata non lo chiariva.

+2

Si prega di tenere presente che "se la variabile che viene assegnata da è indefinita" è SBAGLIATA. "se la variabile assegnata da è falsa" è molto più vicina alla verità. – innaM

+1

Per seguire il commento di Manni: è meglio usare l'operatore definito o "//" se si può fare affidamento su Perl 5.10 o successivo. (Non che questo abbia qualche influenza su come lo faresti in Python.) –

risposta

26

Dal 2.5:

Se si vuole ripiegare solo su Nessuno:

a = x if x is not None else y 

Se si vuole ripiegare anche su stringa vuota, false, 0 ecc .:

a = x if x else y 

o

a = x or y 

Per quanto riguarda indefinito (come mai definito, alias non vincolato):

try: 
    a = x 
except NameError: 
    a = y 

o un po 'più hackish (non piacerebbe davvero consiglio, ma è breve):

a = vars().get('x',y) 
+0

Molto meglio del mio ... – Greg

+0

hm, quindi x è definito dopo tutto:/ – SilentGhost

+0

@greg: non è migliore del tuo ma è ugualmente sbagliato – SilentGhost

1

C'è operazione ternario di pitone:

a = x if x is not None else y 

Disponibile dalla 2.5 in poi.

-1

Se si tratta di un argomento a una funzione si può fare questo:

def MyFunc(a=2): 
    print "a is %d"%a 

>>> MyFunc() 
...a is 2 
>>> MyFunc(5) 
...a is 5 

[Edit] Per i downvoters .. il if/else bit è inutile per la soluzione - appena aggiunto per rendere i risultati chiaro. Modificato per rimuovere la dichiarazione if se questo lo rende più chiaro?

+0

Non ho idea di cosa ti abbia costretto ad aggiungere il if else in MyFunc. Se uccidi il blocco if, else e else, tutto funziona ancora allo stesso modo. –

+0

Certo che lo fa - stavo solo cercando di illustrare i risultati! –

+0

+1 per neutralizzare da quando hai corretto. :) –

5

prima si può fare il vostro full-on con un ternario:

a = y if x is None else x 

ma non risolve il problema.ciò che si vuole fare è più strettamente implementata con:

try: 
    a = x 
except: 
    a = y 
+0

Scusa, sì, questo non era in realtà quello che stavo cercando, ma è una buona risposta alla domanda che ho effettivamente chiesto! – maxaposteriori

+0

@Andy: leggi il commento più upvoted alla risposta accettata per capire perché non risponde alla tua domanda in realtà – SilentGhost

+0

Questa soluzione sembra il modo corretto per farlo. Potresti renderlo più conciso semplicemente: provare: a = x eccetto: a = y – randlet

0

maggior parte delle soluzioni basandosi su dichiarazioni se non funzionano per il caso in cui x è 0 o negativo.

>>> x = 0 
>>> y = 2 
>>> a = x or y 
>>> a 
2 
>>> 

Se tu conoscessi il nome della variabile prima del tempo si potrebbe cercare in questo modo:

if 'x' in dir(): 
    a = x 
except: 
    a =y 

Tuttavia questa soluzione sembra tipo di sciatta per me. Credo che il metodo migliore è quello di utilizzare una prova eccetto il blocco in questo modo:

try: 
    a = x 
else: 
    a = y 
3

Sono abbastanza convinto che non esiste un modo 'divinatorio' per fare questo, perché questo non è un modello che è divinatorio. Il controllo non dovrebbe raggiungere un riferimento variabile non definito nel codice elegante. Ci sono idee simili che sono pythonic. Più ovvia:

def myRange(start, stop=None): 
    start, stop = (0, start) if stop is None else (start, stop) 
    ... 

Che cosa è importante è che stop è definito nella portata, ma il chiamante non ha dovuto passarlo in modo esplicito, ma solo che ha preso il suo valore di default, alterando la semantica degli argomenti, che in effetto fa sì che il primo argomento sia opzionale invece del secondo, anche se il linguaggio non lo consente senza questo trucco intelligente.

Detto questo, qualcosa del genere potrebbe seguire la premessa senza utilizzare un blocco try-catch.

a = locals().get('x', y) 
+0

+1 concordato al 100% quando dici "perché questo non è uno schema che è pitonico" –

4

Solo un po 'fare i difficili con il Perl ad esempio:

my $x = undef; 

Questo codice ridondante può essere abbreviato a:

my $x; 

E il seguente codice non fare quello che dici che fa :

my $a = $x || $y; 

Th in realtà assegna $ y a $ a quando $ x è false. I valori falsi includono cose come undef, zero e la stringa vuota. Per unico test per definedness, si potrebbe fare la seguente (come di Perl 5.10):

my $a = $x // $y; 
0

Un modo per riscrivere ...

if x is not None 
    a = x 
else 
    a = y 

..è:

x = myfunction() 

if x is None: 
    x = y 

print x 

Oppure, usare le eccezioni (forse più Python'y, a seconda della ciò che il codice sta facendo - se restituisce None, perché c'è stato un errore, usando un'eccezione è probabilmente il modo corretto):

try: 
    x = myfunction() 
except AnException: 
    x = "fallback" 

print x 

detto questo, non c'è davvero niente di sbagliato con te codice originale:

if x is not None 
    a = x 
else 
    a = y 

è lunga, ma trovo che molto più facile da leggere (e molto più Pythonic) di una delle due follo wing one-liners:

a = x if x is not None else y 
a = x or y 
Problemi correlati