Python: try-Anweisung in einer einzelnen Zeile

89

Gibt es in Python eine Möglichkeit, einen Versuch / eine Ausnahme in eine einzelne Zeile umzuwandeln?

etwas wie...

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

Wo bist eine deklarierte Variable und cist nicht ... cwürde also einen Fehler auslösen und awürde b...

Brant
quelle

Antworten:

60

In Python gibt es keine Möglichkeit, einen try/ except-Block in eine einzelne Zeile zu komprimieren .

Es ist auch eine schlechte Sache, nicht zu wissen, ob eine Variable in Python vorhanden ist, wie Sie es in einigen anderen dynamischen Sprachen tun würden. Der sicherere Weg (und der vorherrschende Stil) besteht darin, alle Variablen auf etwas zu setzen. Wenn sie möglicherweise nicht festgelegt werden, setzen Sie sie Nonezuerst (oder 0oder ''oder etwas, wenn dies zutreffender ist).


Wenn Sie tun alle die Namen assign Sie zuerst interessiert sind, haben Sie Optionen.

  • Die beste Option ist eine if-Anweisung.

    c = None
    b = [1, 2]
    
    if c is None:
        a = b
    else:
        a = c
    
  • Die Einzeileroption ist ein bedingter Ausdruck.

    c = None
    b = [1, 2]
    a = c if c is not None else b
    
  • Einige Leute missbrauchen das Kurzschlussverhalten or, um dies zu tun. Dies ist fehleranfällig, daher verwende ich es nie.

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

    Betrachten Sie den folgenden Fall:

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

    In diesem Fall sollte es awahrscheinlich sein , aber es liegt daran, dass es in einem booleschen Kontext falsch ist. Da es viele Werte gibt, die falsch sein können, verwende ich den Trick nicht. (Dies ist das gleiche Problem, auf das Menschen stoßen, wenn sie sagen, wann sie meinen .)[][1, 2][]orif foo:if foo is not None:

Mike Graham
quelle
Vielen Dank. Das Problem ist, dass es tatsächlich eine Abfrage von django model.objects.get ist, die ich zu testen versuche. Das .get gibt einen Fehler zurück, wenn keine Daten gefunden werden ... es gibt kein None zurück (was mich nervt)
Brant
@Brant, Okay, diese Situation unterscheidet sich ein wenig von der Überprüfung, ob eine Variable festgelegt ist (in Python werden keine Variablen deklariert). Der typische Stil in Python besteht darin, Ausnahmen zu erheben, anstatt Fehler als Werte zurückzugeben, die viele von uns tatsächlich lieben. Wenn ich den Rückkehrcode einer Operation jedes Mal überprüfen muss und es schwierig ist, Fehler aufzuspüren, wenn ich dies nicht tue, vermisse ich C beim Schreiben von Python definitiv nicht. In jedem Fall gibt es, obwohl dies bereits besprochen wurde, keine einzeilige Syntax für einen try/ exceptBlock. Glücklicherweise sind Leitungen billig, daher sollte die 4-Zeilen-Lösung für Sie funktionieren. ;-)
Mike Graham
Es ist Teil einer großen Anzahl von Tupeln innerhalb eines Diktats ... Ich habe nur versucht, die Dinge ein wenig zu verkürzen
Brant
2
Nicht verwenden, getwenn Sie keine Ausnahme wünschen. Verwenden Sie filterstattdessen.
JCDyer
@ MikeGraham Gute Antwort - ein Hinweis (Link?), Warum der Kurzschluss fehleranfällig ist, wäre schön.
Kratenko
83

Das ist furchtbar hackisch, aber ich habe es an der Eingabeaufforderung verwendet, als ich eine Folge von Aktionen zum Debuggen schreiben wollte:

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

Zum größten Teil stört mich die Einschränkung, dass nur einzeiliger Versuch ausgeführt werden darf, überhaupt nicht, aber wenn ich nur experimentiere und möchte, dass readline einen ganzen Codeblock auf einmal im interaktiven Interpreter aufruft dass ich es irgendwie einstellen kann, ist dieser kleine Trick praktisch.

Für den eigentlichen Zweck, den Sie erreichen möchten, könnten Sie es versuchen locals().get('c', b); Im Idealfall ist es besser, ein echtes Wörterbuch anstelle des lokalen Kontexts zu verwenden oder einfach c c None zuzuweisen, bevor Sie ausführen, was auch immer festgelegt ist oder nicht.

Walter Mundt
quelle
26
Hey, das beantwortet die Frage! :)
Steve Bennett
4
Ich liebe diese Antwort, super chaotisch, aber eine Zeile, genau so, wie ich es mag.
Patrick Cook
Das ist die Antwort!! wird problem[0]zurückgeben, was diese Funktion zurückgibt?
Islam
4
Exec ist ein Codegeruch und sollte vermieden werden, es sei denn, nichts anderes funktioniert. Wenn ein Zeilencode so wichtig ist, funktioniert dies, aber Sie müssen sich fragen, warum eine Zeile so wichtig ist.
Gewthen
4
Natürlich nicht für die Produktion, sondern genau das, was für eine umständliche Debugging-Sitzung benötigt wird.
ThorSummoner
45

In python3 können Sie verwenden contextlib.suppress :

from contextlib import suppress

d = {}
with suppress(KeyError): d['foo']
dset0x
quelle
8
Dies sollte die Standardantwort sein
Sphynx-HenryAY
13

Eine andere Möglichkeit besteht darin, einen Kontextmanager zu definieren:

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

Verwenden Sie dann die withAnweisung, um Fehler in einer einzelnen Zeile zu ignorieren:

>>> 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

Im Falle eines Laufzeitfehlers wird keine Ausnahme ausgelöst. Es ist wie try:ohne except:.

Bitagoras
quelle
1
Das ist toll! Können Sie kurz erklären, wie der Kontextmanager mit Fehlern umgeht, da es keinen expliziten Versuch / außer gibt?
Patrick
8

Version von poke53280 Antwort mit begrenzten erwarteten Ausnahmen.

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

und es könnte als verwendet werden

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
Pavlo Pravdiukov
quelle
Wofür steht das Komma in "expected_exc = (Exception,)"? Kannst du bitte erklären?
Ibilgen
7
parse_float = lambda x, y=exec("def f(s):\n try:\n  return float(s)\n except:  return None"): f(x)

Es gibt immer eine Lösung.

Miklos Horvath
quelle
5

Das Problem ist, dass es tatsächlich eine Abfrage von django model.objects.get ist, die ich zu testen versuche. Das .get gibt einen Fehler zurück, wenn keine Daten gefunden wurden. Es gibt kein None zurück (was mich nervt.)

Verwenden Sie so etwas:

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

Wobei try_or eine von Ihnen definierte Dienstprogrammfunktion ist:

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

Optional können Sie die akzeptierten Ausnahmetypen beschränken NameError, AttributeErrorusw.

poke53280
quelle
4

Sie können es tun , indem Sie den Namespace dict Zugriff mit vars(), locals()oder globals(), je nachdem , was für Ihre Situation am besten geeignet ist.

>>> b = 'some variable'
>>> a = vars().get('c', b)
jcdyer
quelle
3
Dies funktioniert nicht genau so, als würde überprüft, ob eine Variable festgelegt ist (obwohl dies der Fall ist, wenn Sie an einem bestimmten Bereich interessiert sind). Außerdem ewwwwwwww .....
Mike Graham
4

Wie wäre es mit zwei Zeilen. Ist es o.k ?

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

Sie haben erwähnt, dass Sie Django verwenden. Wenn es für das, was Sie tun, Sinn macht, möchten Sie vielleicht Folgendes verwenden:

my_instance, created = MyModel.objects.get_or_create()

createdwird wahr oder falsch sein. Vielleicht hilft dir das.

Blokeley
quelle
1

Wenn Sie tatsächlich Ausnahmen verwalten müssen:
(geändert von der Antwort von 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
>>> 

Beachten Sie, dass die Ausnahme, wenn sie nicht unterstützt wird, wie erwartet ausgelöst wird:

>>> 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'
>>> 

auch wenn Exceptionangegeben, wird es alles unten übereinstimmen.
( BaseExceptionist höher, daher wird es nicht übereinstimmen)

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

Arbeitet an Python3, inspiriert von Walter Mundt

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

Für mehrere Zeilen in eine Zeile

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

Ps: Exec ist unsicher für Daten, über die Sie keine Kontrolle haben.

Punnerud
quelle