web-dev-qa-db-fra.com

Python: instruction try sur une seule ligne

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

b est une variable déclarée et c n'est pas ... alors c renvoie une erreur et a devient b...

58
Brant

Il n’existe aucun moyen de compresser un bloc tryexcept sur une seule ligne en Python.

De plus, il est mauvais 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 dominant) est de définir toutes les variables sur quelque chose. Si elles ne sont pas configurées, définissez-les d'abord sur None (ou 0 ou '' ou quelque chose si cela s'applique mieux).


Si vous faites assignez d’abord tous les noms qui vous intéressent, 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 or en court-circuitant. C'est sujet aux erreurs, donc je ne l'utilise jamais.

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

    Considérons le cas suivant:

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

    Dans ce cas, a probablement _/devrait être [], mais il s'agit de [1, 2] car [] 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 or. (C'est le même problème que les gens rencontrent quand ils disent if foo: quand ils veulent dire if foo is not None:.)

43
Mike Graham

C'est terriblement bidon, 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 gêné par la restriction no-line-line-try-except, mais lorsque je fais des essais et que je veux que readline rappelle un morceau de code à la fois dans l'interpréteur interactif que je puisse l’ajuster d’une manière ou d’une autre, cette petite astuce est très utile.

Pour le but réel que vous essayez d’atteindre, vous pouvez essayer locals().get('c', b); Dans l’idéal, il serait préférable d’utiliser un dictionnaire réel plutôt que le contexte local ou d’attribuer simplement c à Aucun avant d’exécuter ce que vous voulez ou non.

64
Walter Mundt

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

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

Utilisez ensuite l'instruction with 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:.

6
bitagoras

Vous pouvez le faire en accédant à l'espace de noms en utilisant vars(), locals() ou globals(), selon ce qui convient le mieux à votre situation.

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

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'énerve)

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

Vous pouvez éventuellement limiter les types d’exception acceptés à NameError, AttributeError, etc.

2
poke53280

Vous avez mentionné que vous utilisez Django. Si cela vous semble judicieux, vous voudrez peut-être utiliser:

my_instance, created = MyModel.objects.get_or_create()

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

2
blokeley
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.

2
Miklos Horvath

En python3, vous pouvez utiliser contextlib.suppress :

from contextlib import suppress

d = {}
with suppress(KeyError): d['foo']
1
dset0x

Version de poke53280 avec un nombre limité d’exceptions attendues.

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
0
Pavlo Pravdiukov

si vous avez réellement besoin de gérer des 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 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'
>>> 

de plus, si Exception est donné, cela correspondra à tout.
(BaseException est plus élevé, donc il ne correspondra pas)

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