Python: istruzione try in una singola riga


97

C'è un modo in Python per trasformare una prova / tranne in una singola riga?

qualcosa di simile a...

b = 'some variable'
a = c | b #try statement goes here

Dove bè una variabile dichiarata e cnon lo è ... quindi cgenererebbe un errore e adiventerebbe b...

Risposte:


63

Non c'è modo di comprimere un blocco try/ exceptsu una singola riga in Python.

Inoltre, è una brutta cosa non sapere se esiste una variabile in Python, come faresti in altri linguaggi dinamici. Il modo più sicuro (e lo stile prevalente) è impostare tutte le variabili su qualcosa. Se potrebbero non essere impostati, impostali per Noneprimi ( 0o ''o qualcosa del genere se è più applicabile).


Se fai assegnare tutti i nomi a cui sei interessato prima, avete opzioni.

  • L'opzione migliore è un'istruzione if.

    c = None
    b = [1, 2]
    
    if c is None:
        a = b
    else:
        a = c
    
  • L'opzione di una riga è un'espressione condizionale.

    c = None
    b = [1, 2]
    a = c if c is not None else b
    
  • Alcune persone abusano del comportamento di cortocircuito di orper farlo. Questo è soggetto a errori, quindi non lo uso mai.

    c = None
    b = [1, 2]
    a = c or b
    

    Considera il seguente caso:

    c = []
    b = [1, 2]
    a = c or b
    

    In questo caso, aprobabilmente dovrebbe esserlo [], ma è [1, 2]perché []è falso in un contesto booleano. Poiché ci sono molti valori che possono essere falsi, non uso il ortrucco. (Questo è lo stesso problema che le persone incontrano quando dicono if foo:quando intendono if foo is not None:.)


Grazie. Il problema è che in realtà è una query django model.objects.get che sto cercando di testare. il .get restituisce un errore se non vengono trovati dati ... non restituisce None (cosa che mi infastidisce)
Brant

@Brant, Ok, quella situazione è leggermente diversa dal controllare se una variabile è impostata (nessuna variabile è dichiarata in Python). Lo stile tipico in Python è quello di preferire la creazione di eccezioni alla restituzione di errori come valori, cosa che molti di noi amano davvero. Dover controllare ogni volta il codice di ritorno di un'operazione e avere difficoltà a rintracciare gli errori se non lo faccio è qualcosa che non mi manca di C quando scrivo Python. In ogni caso, sebbene sia stato discusso, non esiste una sintassi su una riga per un blocco try/ except. Fortunatamente le linee sono economiche, quindi la soluzione a 4 linee dovrebbe funzionare per te. ;-)
Mike Graham

Fa parte di un ampio insieme di tuple all'interno di un dict ... Stavo solo cercando di accorciare un po 'le cose
Brant

2
Non usare getse non vuoi un'eccezione. Usa filterinvece.
jcdyer

@ MikeGraham Buona risposta - un suggerimento (collegamento?) Perché il cortocircuito è soggetto a errori sarebbe bello.
kratenko

85

Questo è terribilmente hackish, ma l'ho usato al prompt quando volevo scrivere una sequenza di azioni per il debug:

exec "try: some_problematic_thing()\nexcept: problem=sys.exc_info()"
print "The problem is %s" % problem[1]

Per la maggior parte, non sono affatto infastidito dalla restrizione non-singola-riga-try-tranne, ma quando sto solo sperimentando e voglio che readline richiami un intero pezzo di codice in una volta nell'interprete interattivo quindi che posso regolarlo in qualche modo, questo piccolo trucco è utile.

Per lo scopo effettivo che stai cercando di raggiungere, potresti provare locals().get('c', b); idealmente sarebbe meglio usare un dizionario reale invece del contesto locale, o semplicemente assegnare c a None prima di eseguire qualsiasi cosa possa o non possa impostarlo.


26
Ehi, questo risponde alla domanda! :)
Steve Bennett

4
Adoro questa risposta, super confusa, ma una riga, proprio come piace a me.
Patrick Cook

Questa è la risposta !! sarà problem[0]restituire ciò che restituisce la funzione?
SIslam

5
Exec è un odore di codice e dovrebbe essere evitato a meno che nient'altro funzioni. Se un codice di riga è così importante, funzionerà, ma è necessario chiedersi perché una riga è così importante.
Gewthen

4
chiaramente non per uso di produzione, ma esattamente ciò che è necessario per una scomoda sessione di debug.
ThorSummoner


13

Un altro modo è definire un gestore di contesto:

class trialContextManager:
    def __enter__(self): pass
    def __exit__(self, *args): return True
trial = trialContextManager()

Quindi utilizzare l' withistruzione per ignorare gli errori in una singola riga:

>>> with trial: a = 5      # will be executed normally
>>> with trial: a = 1 / 0  # will be not executed and no exception is raised
>>> print a
5

Non verrà sollevata alcuna eccezione in caso di errore di runtime. È come un try:senza il except:.


1
È fantastico! Dato che non ci sono prove / eccezioni esplicite, potresti spiegare brevemente come il gestore del contesto gestisce gli errori?
Patrick,

10

Versione della risposta poke53280 con limitate eccezioni previste.

def try_or(func, default=None, expected_exc=(Exception,)):
    try:
        return func()
    except expected_exc:
        return default

e potrebbe essere usato come

In [2]: try_or(lambda: 1/2, default=float('nan'))
Out[2]: 0.5

In [3]: try_or(lambda: 1/0, default=float('nan'), expected_exc=(ArithmeticError,))
Out[3]: nan

In [4]: try_or(lambda: "1"/0, default=float('nan'), expected_exc=(ArithmeticError,))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
[your traceback here]
TypeError: unsupported operand type(s) for /: 'str' and 'int'

In [5]: try_or(lambda: "1"/0, default=float('nan'), expected_exc=(ArithmeticError, TypeError))
Out[5]: nan

A cosa serve la virgola in "expected_exc = (Exception,)"? Puoi spiegare, per favore?
ibilgen

1
@ibilgen La virgola converte l'espressione in una tupla . Scrivere (Exception)equivale a tralasciare le parentesi. (Exception, )dice all'interprete che questa è una tupla (qualcosa come una lista) con una voce. In questo esempio viene utilizzato quindi expected_excpuò essere più di un'eccezione.
miile7

7
parse_float = lambda x, y=exec("def f(s):\n try:\n  return float(s)\n except:  return None"): f(x)

C'è sempre una soluzione.


5

Il problema è che in realtà è una query django model.objects.get che sto cercando di testare. il .get restituisce un errore se non viene trovato alcun dato ... non restituisce None (il che mi infastidisce)

Usa qualcosa del genere:

print("result:", try_or(lambda: model.objects.get(), '<n/a>'))

Dove try_or è una funzione di utilità definita da te:

def try_or(fn, default):
    try:
        return fn()
    except:
        return default

Opzionalmente è possibile limitare i tipi di eccezione hanno accettato di NameError, AttributeErrorecc


5

Che ne dici di usare due linee. va bene ?

>>> try: a = 3; b= 0; c = a / b
... except : print('not possible'); print('zero division error')
...
not possible
zero division error

4

È possibile farlo accedendo alla dict namespace utilizzando vars(), locals()o globals(), a seconda di quale è più appropriato per la situazione.

>>> b = 'some variable'
>>> a = vars().get('c', b)

3
Questo non funziona esattamente come controllare se una variabile è impostata (anche se lo fa se sei interessato a un particolare ambito.) Inoltre, ewwwwwwww .....
Mike Graham

2

Hai detto che stai usando Django. Se ha senso per quello che stai facendo potresti voler usare:

my_instance, created = MyModel.objects.get_or_create()

createdsarà vero o falso. Forse questo ti aiuterà.


2

Funziona su Python3, ispirato da Walter Mundt

exec("try:some_problematic_thing()\nexcept:pass")

Per più linee in una linea

exec("try:\n\tprint('FirstLineOk')\n\tsome_problematic_thing()\n\tprint('ThirdLineNotTriggerd')\nexcept:pass")

Ps: Exec non è sicuro da usare su dati su cui non hai il controllo.


1

se è necessario gestire effettivamente le eccezioni:
(modificato dalla risposta di poke53280)

>>> def try_or(fn, exceptions: dict = {}):
    try:
        return fn()
    except Exception as ei:
        for e in ei.__class__.__mro__[:-1]:
            if e in exceptions: return exceptions[e]()
        else:
            raise


>>> def context():
    return 1 + None

>>> try_or( context, {TypeError: lambda: print('TypeError exception')} )
TypeError exception
>>> 

nota che se l'eccezione non è supportata, verrà sollevata come previsto:

>>> try_or( context, {ValueError: lambda: print('ValueError exception')} )
Traceback (most recent call last):
  File "<pyshell#57>", line 1, in <module>
    try_or( context, {ValueError: lambda: print('ValueError exception')} )
  File "<pyshell#38>", line 3, in try_or
    return fn()
  File "<pyshell#56>", line 2, in context
    return 1 + None
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
>>> 

inoltre, se Exceptionviene fornito, corrisponderà a qualsiasi cosa sotto.
( BaseExceptionè più alto, quindi non corrisponderà)

>>> try_or( context, {Exception: lambda: print('exception')} )
exception

0

Ecco una versione più semplice della risposta fornita da @surendra_ben

a = "apple"try: a.something_that_definitely_doesnt_exist
except: print("nope")

...

nope

0

Usa la withsintassi in una riga:

class OK(): __init__ = lambda self, *isok: setattr(self, 'isok', isok); __enter__ = lambda self: None; __exit__ = lambda self, exc_type, exc_value, traceback: (True if not self.isok or issubclass(exc_type, self.isok) else None) if exc_type else None

Ignora eventuali errori:

with OK(): 1/0

Ignora gli errori specificati:

with OK(ZeroDivisionError, NameError): 1/0
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.