Python: instrukcja try w jednym wierszu

89

Czy w Pythonie jest sposób, aby zamienić try / except w pojedynczą linię?

coś jak...

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

Gdzie bjest zadeklarowaną zmienną i cnie jest ... więc czgłosi błąd i astanie się b...

Brant
źródło

Odpowiedzi:

60

W Pythonie nie ma sposobu, aby skompresować try/ exceptblok do jednej linii.

Poza tym źle jest nie wiedzieć, czy w Pythonie istnieje zmienna, tak jak w innych dynamicznych językach. Bezpieczniejszym sposobem (i dominującym stylem) jest ustawienie na coś wszystkich zmiennych. Jeśli mogą nie zostać ustawione, ustaw je jako Nonepierwsze ( 0lub ''lub coś, jeśli jest to bardziej odpowiednie).


Jeśli zrobić przypisać wszystkie nazwiska jesteś zainteresowany po pierwsze, masz opcje.

  • Najlepszą opcją jest instrukcja if.

    c = None
    b = [1, 2]
    
    if c is None:
        a = b
    else:
        a = c
    
  • Opcja jednowierszowa jest wyrażeniem warunkowym.

    c = None
    b = [1, 2]
    a = c if c is not None else b
    
  • Niektórzy ludzie nadużywają w tym celu zachowania zwarciowego or. Jest to podatne na błędy, więc nigdy go nie używam.

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

    Rozważ następujący przypadek:

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

    W tym przypadku aprawdopodobnie powinno być [], ale dzieje się tak , [1, 2]ponieważ []jest fałszywe w kontekście logicznym. Ponieważ istnieje wiele wartości, które mogą być fałszywe, nie używam orsztuczki. (Jest to ten sam problem, na który napotykają ludzie, kiedy mówią, if foo:kiedy mają na myśli if foo is not None:).

Mike Graham
źródło
Dzięki. Problem polega na tym, że jest to faktycznie zapytanie model.objects.get django, które próbuję przetestować. .get zwraca błąd, jeśli nie znaleziono żadnych danych ... nie zwraca None (co mnie denerwuje)
Brant
@Brant, OK, ta sytuacja jest nieco inna niż sprawdzenie, czy zmienna jest ustawiona (w Pythonie nie ma zadeklarowanych zmiennych). Typowym stylem w Pythonie jest preferowanie zgłaszania wyjątków od zwracania błędów jako wartości, co wielu z nas naprawdę lubi. Konieczność sprawdzania kodu powrotnego operacji za każdym razem i trudności w wyśledzeniu błędów, jeśli tego nie robię, to coś, czego zdecydowanie nie brakuje w C podczas pisania w Pythonie. W każdym razie, chociaż zostało to omówione, nie ma jednowierszowej składni dla bloku try/ except. Na szczęście linie są tanie, więc rozwiązanie 4-liniowe powinno działać dla Ciebie. ;-)
Mike Graham
Jest to część dużego zestawu krotek w dyktandzie ... Chciałem tylko trochę skrócić
Brant
2
Nie używaj, getjeśli nie chcesz wyjątku. Użyj filterzamiast tego.
jcdyer
@MikeGraham Dobra odpowiedź - wskazówka (link?), Dlaczego zwarcie jest podatne na błędy, byłoby miło.
kratenko
83

To strasznie hackerskie, ale użyłem go w zachęcie, gdy chciałem napisać sekwencję działań do debugowania:

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

W większości przypadków w ogóle nie przeszkadza mi ograniczenie bez jednej linii-try-z wyjątkiem, ale kiedy tylko eksperymentuję i chcę, aby readline przypomniało sobie cały fragment kodu naraz w interaktywnym interpreteru, więc że mogę to jakoś dostosować, ta mała sztuczka się przydaje.

W celu, jaki chcesz osiągnąć, możesz spróbować locals().get('c', b); Idealnie byłoby lepiej użyć prawdziwego słownika zamiast lokalnego kontekstu lub po prostu przypisać c do Brak przed uruchomieniem tego, co może go ustawić lub nie.

Walter Mundt
źródło
26
Hej, to odpowiada na pytanie! :)
Steve Bennett
4
Uwielbiam tę odpowiedź, super niechlujna, ale jedna linijka, tak jak lubię.
Patrick Cook
To jest odpowiedź !! będzie problem[0]wrócić co zwracającej funkcji?
SIslam
4
Exec to zapach kodu i należy go unikać, chyba że nic innego nie działa. Jeśli kod jednej linii jest tak ważny, to zadziała, ale musisz zadać sobie pytanie, dlaczego jedna linia jest tak ważna.
Gewthen
4
najwyraźniej nie do użytku produkcyjnego, ale dokładnie to, co jest potrzebne do niezręcznej sesji debugowania.
ThorSummoner
46

W pythonie3 możesz użyć contextlib.suppress :

from contextlib import suppress

d = {}
with suppress(KeyError): d['foo']
dset0x
źródło
8
to powinna być standardowa odpowiedź
Sphynx-HenryAY
13

Innym sposobem jest zdefiniowanie menedżera kontekstu:

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

Następnie użyj withinstrukcji, aby zignorować błędy w jednym wierszu:

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

Żaden wyjątek nie zostanie zgłoszony w przypadku błędu w czasie wykonywania. To jest jak try:bez except:.

bitagoras
źródło
1
To jest świetne! Ponieważ nie ma wyraźnych prób / wyjątków, czy możesz krótko wyjaśnić, jak menedżer kontekstu radzi sobie z błędami?
Patrick,
8

Wersja odpowiedzi poke53280 z ograniczonymi oczekiwanymi wyjątkami.

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

i może być używany jako

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
źródło
Po co jest przecinek w wyrażeniu „spodziewany_exc = (wyjątek,)”? Czy możesz wyjaśnić, proszę?
ibilgen
7
parse_float = lambda x, y=exec("def f(s):\n try:\n  return float(s)\n except:  return None"): f(x)

Zawsze jest rozwiązanie.

Miklos Horvath
źródło
5

Problem polega na tym, że jest to faktycznie zapytanie model.objects.get django, które próbuję przetestować. .get zwraca błąd, jeśli nie znaleziono żadnych danych ... nie zwraca None (co mnie denerwuje)

Użyj czegoś takiego:

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

Gdzie try_or to zdefiniowana przez Ciebie funkcja narzędziowa:

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

Opcjonalnie można ograniczyć Akceptowane formaty wyjątek NameError, AttributeErroritp

poke53280
źródło
4

Można to zrobić, korzystając z dict namespace użyciu vars(), locals()lub globals(), w zależności co jest najbardziej odpowiednie dla danej sytuacji.

>>> b = 'some variable'
>>> a = vars().get('c', b)
jcdyer
źródło
3
Nie działa to dokładnie tak samo, jak sprawdzanie, czy zmienna jest ustawiona (chociaż tak jest, jeśli interesuje Cię konkretny zakres). Ponadto, ewwwwwwww .....
Mike Graham
4

Co powiesz na użycie dwóch linii. czy to jest w porządku ?

>>> try: a = 3; b= 0; c = a / b
... except : print('not possible'); print('zero division error')
...
not possible
zero division error
surendra ben
źródło
2

Wspomniałeś, że używasz django. Jeśli ma to sens dla tego, co robisz, możesz użyć:

my_instance, created = MyModel.objects.get_or_create()

createdbędzie True lub False. Może to ci pomoże.

blokeley
źródło
1

jeśli chcesz faktycznie zarządzać wyjątkami:
(zmodyfikowane na podstawie odpowiedzi 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
>>> 

zauważ, że jeśli wyjątek nie jest obsługiwany, zostanie podniesiony zgodnie z oczekiwaniami:

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

również jeśli Exceptionzostanie podane, będzie pasować do wszystkiego poniżej.
( BaseExceptionjest wyższa, więc nie będzie pasować)

>>> try_or( context, {Exception: lambda: print('exception')} )
exception
Tcll
źródło
1

Działa na Pythonie3, inspirowanym twórczością Waltera Mundta

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

Dla wielu linii w jedną linię

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

Ps: Exec jest niebezpieczny w przypadku danych, nad którymi nie masz kontroli.

Punnerud
źródło