Python: instruction try en une seule ligne

89

Existe-t-il un moyen en python de transformer un essai / sauf en une seule ligne?

quelque chose comme...

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

best une variable déclarée et cn'est pas ... donc clancerait une erreur et adeviendrait b...

Brant
la source

Réponses:

60

Il n'y a aucun moyen de compresser un bloc try/ exceptsur une seule ligne en Python.

De plus, c'est une mauvaise chose de ne pas savoir si une variable existe en Python, comme vous le feriez dans d'autres langages dynamiques. La manière la plus sûre (et le style qui prévaut) est de définir toutes les variables sur quelque chose. S'ils ne sont pas configurés, définissez-les d' Noneabord (ou 0ou ''ou quelque chose si c'est plus applicable.)


Si vous faites assign tous les noms qui vous intéressent d' abord, vous avez des options.

  • La meilleure option est une instruction if.

    c = None
    b = [1, 2]
    
    if c is None:
        a = b
    else:
        a = c
    
  • L'option one-liner est une expression conditionnelle.

    c = None
    b = [1, 2]
    a = c if c is not None else b
    
  • Certaines personnes abusent du comportement de court-circuit de orpour ce faire. Ceci est sujet aux erreurs, donc je ne l'utilise jamais.

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

    Prenons le cas suivant:

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

    Dans ce cas, cela devraita probablement l' être , mais c'est parce que c'est faux dans un contexte booléen. Parce qu'il y a beaucoup de valeurs qui peuvent être fausses, je n'utilise pas l' astuce. (C'est le même problème que les gens rencontrent lorsqu'ils disent quand ils veulent dire .)[][1, 2][]orif foo:if foo is not None:

Mike Graham
la source
Merci. Le problème est que c'est en fait une requête django model.objects.get que j'essaie de tester. le .get renvoie une erreur si aucune donnée n'est trouvée ... il ne renvoie aucun (ce qui m'ennuie)
Brant
@Brant, d'accord, cette situation est un peu différente de celle de vérifier si une variable est définie (aucune variable n'est déclarée en Python). Le style typique en Python est de préférer lever des exceptions au renvoi d'erreurs sous forme de valeurs, ce que beaucoup d'entre nous adorent. Devoir vérifier le code de retour d'une opération à chaque fois et avoir du mal à localiser les erreurs si je ne le fais pas est quelque chose que je ne manque certainement pas à propos de C lors de l'écriture de Python. Dans tous les cas, bien que cela ait été discuté, il n'y a pas de syntaxe sur une ligne pour un bloc try/ except. Heureusement, les lignes sont bon marché, donc la solution à 4 lignes devrait fonctionner pour vous. ;-)
Mike Graham
Cela fait partie d'un grand ensemble de tuples dans un dict ... J'essayais juste de raccourcir un peu les choses
Brant
2
Ne l'utilisez pas getsi vous ne voulez pas d'exception. Utilisez filterplutôt.
jcdyer
@MikeGraham Bonne réponse - un indice (lien?) Pourquoi le court-circuit est sujet aux erreurs serait bien.
kratenko
83

C'est terriblement hackish, mais je l'ai utilisé à l'invite lorsque je voulais écrire une séquence d'actions pour le débogage:

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

Pour la plupart, je ne suis pas du tout dérangé par la restriction no-single-line-try-except, mais quand je fais juste des expériences et que je veux que readline rappelle tout un morceau de code à la fois dans l'interpréteur interactif, donc que je peux l'ajuster d'une manière ou d'une autre, cette petite astuce est très pratique.

Pour le but réel que vous essayez d'accomplir, vous pouvez essayer locals().get('c', b); idéalement, il serait préférable d'utiliser un vrai dictionnaire au lieu du contexte local, ou simplement d'assigner c à None avant d'exécuter ce que vous pouvez ou ne pouvez pas définir.

Walter Mundt
la source
26
Hé, cela répond à la question! :)
Steve Bennett
4
J'adore cette réponse, super désordonnée, mais une ligne, juste comme je l'aime.
Patrick Cook
C'est la réponse !! va problem[0]revenir ce que la fonction retourne?
SIslam
4
Exec est une odeur de code et doit être évité à moins que rien d'autre ne fonctionne. Si un code de ligne est si important, cela fonctionnera, mais vous devez vous demander pourquoi une ligne est si importante.
Gewthen
4
clairement pas pour une utilisation en production, mais exactement ce qui est nécessaire pour une session de débogage maladroite.
ThorSummoner
46

En python3, vous pouvez utiliser contextlib.suppress :

from contextlib import suppress

d = {}
with suppress(KeyError): d['foo']
dset0x
la source
8
cela devrait être la réponse standard
Sphynx-HenryAY
13

Une autre façon est de définir un gestionnaire de contexte:

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

Ensuite, utilisez l' withinstruction pour ignorer les erreurs sur une seule ligne:

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

Aucune exception ne sera déclenchée en cas d'erreur d'exécution. C'est comme un try:sans le except:.

bitagores
la source
1
C'est bien! Puisqu'il n'y a aucun essai explicite / sauf, pourriez-vous expliquer brièvement comment le gestionnaire de contexte traite les erreurs?
Patrick
8

Version de la réponse poke53280 avec des exceptions attendues limitées.

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

et il pourrait être utilisé comme

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
la source
À quoi sert la virgule dans "expected_exc = (Exception,)"? Pouvez-vous expliquer, s'il vous plaît?
ibilgen le
7
parse_float = lambda x, y=exec("def f(s):\n try:\n  return float(s)\n except:  return None"): f(x)

Il y a toujours une solution.

Miklos Horvath
la source
5

Le problème est que c'est en fait une requête django model.objects.get que j'essaie de tester. le .get renvoie une erreur si aucune donnée n'est trouvée ... il ne renvoie aucune (ce qui m'ennuie)

Utilisez quelque chose comme ceci:

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

Où try_or est une fonction utilitaire définie par vous:

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

En option , vous pouvez limiter les types d'exceptions acceptées à NameError, AttributeErroretc.

poke53280
la source
4

Vous pouvez le faire en accédant à l'espace de noms dict en utilisant vars(), locals()ou globals(), selon le plus approprié à votre situation.

>>> b = 'some variable'
>>> a = vars().get('c', b)
jcdyer
la source
3
Cela ne fonctionne pas exactement de la même manière que de vérifier si une variable est définie (bien que ce soit le cas si vous êtes intéressé par une portée particulière.) Aussi, ewwwwwwww .....
Mike Graham
4

Que diriez-vous d'utiliser deux lignes. Est-ce que c'est bon ?

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

Vous avez mentionné que vous utilisiez django. Si cela a du sens pour ce que vous faites, vous pouvez utiliser:

my_instance, created = MyModel.objects.get_or_create()

createdsera vrai ou faux. Peut-être que cela vous aidera.

Blokeley
la source
1

si vous devez réellement gérer les exceptions:
(modifié de la réponse de 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
>>> 

notez que si l'exception n'est pas prise en charge, elle se déclenchera comme prévu:

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

aussi si Exceptionest donné, il correspondra à tout ce qui suit.
( BaseExceptionest plus élevé, donc il ne correspondra pas)

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

Fonctionne sur Python3, inspiré par Walter Mundt

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

Pour plusieurs lignes en une seule ligne

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

Ps: Exec n'est pas sûr à utiliser sur des données sur lesquelles vous n'avez pas de contrôle.

Punnerud
la source