4
votes

python try sauf en tant que fonction pour évaluer des expressions

J'ai essayé de créer une fonction qui essaie une expression et renvoie zéro si des erreurs se produisent.

def try_or_zero(exp):
    try:
        exp
        return exp
    except:
        return 0

Ce qui ne fonctionne évidemment pas. Il semble que le problème soit que python n'a aucune forme d'évaluation paresseuse, donc l'expression est évaluée avant d'être transmise à la fonction et ainsi il augmente l'erreur avant d'entrer dans la fonction et ne passe donc jamais par la logique try. Est-ce que quelqu'un sait si cela peut être fait en Python? À votre santé


6 commentaires

Qu'est-ce qui est exactement passé à try_or_zero?


vous pouvez le passer sous forme de chaîne puis utiliser eval()


si vous voulez en savoir plus sur l'erreur, utilisez la logging


N'utilisez PAS eval - au moins jusqu'à ce que vous compreniez parfaitement pourquoi vous ne devriez pas l'utiliser.


"Est-ce que quelqu'un sait si cela peut être fait en Python" => oui, il suffit de passer un lambda à votre fonction et de l'appeler depuis la fonction, cf ma réponse.


Je ne comprends vraiment pas les votes serrés ici - la question est parfaitement sur le sujet et assez claire, et contient toutes les informations nécessaires pour y répondre.


4 Réponses :


-2
votes

Passez l'argument à function dans str et exécutez la fonction exec à l'intérieur

def try_or_zero(exp):
    try:
        exec(exp)
        return exp
    except:
        return 0

donc votre appel à la fonction sera comme ci-dessous try_or_zero ('1 == 2')


1 commentaires

Veuillez ne pas le faire. exec et eval sont presque toujours les mauvaises solutions.



0
votes

Pas besoin de s'embêter avec exec et d'autres choses, utilisez le fait que les fonctions python sont des objets et peuvent donc être passées en arguments

def try_or_zero(exp):
    try:
       return exp()
    except:
       return 0

Et appelez simplement try_or_zero(my_awesome_func) (sans le () pour votre méthode)


0 commentaires

-2
votes

Vous pouvez y parvenir en enveloppant vos expressions dans une fonction. Par exemple:

try_or_zero(exp):
    try:
        eval(exp)  # exp must be a string, for example 'raise ValueError'
    except:
        return 0

Votre fonction try devrait également ressembler à ceci:

try_or_zero(ErrorTest)

OutPut: 0

Et mettez-le dans la fonction

 def try_catch(exp):
    try :
        exp()  # note the paranthesis
    except:
        return 0

Vous pouvez également le faire en utilisant la fonction eval (), mais vous devrez mettre votre code dans String.

def ErrorTest():
    # the expression you want to try
    raise Exception


3 commentaires

veuillez ne pas préconiser l'utilisation d' eval .


Pouvez-vous expliquer pourquoi? Est-ce une méthode obsolète? Ou y a-t-il un moyen bien préférable? Merci d'avoir fait remarquer cela!


Les problèmes avec eval (et exec ) ont déjà été expliqués en profondeur à divers endroits. Surtout: ces "fonctionnalités" sont dangereuses (elles exécutent n'importe quel code arbitraire), difficiles à déboguer (vous n'avez pas de traceback appropriée et ne pouvez pas passer par-dessus), et vous obligent à passer le code sous forme de chaîne, ce qui signifie que ce code ne peut pas être linté ni instrumentalisé d'aucune façon. Le point est: 99,999% de ce que eval faire peut se faire de manière documentée, pythonique, plus sûr, lisible et maintenable ( en utilisant un lambda, en utilisant getattr / setattr , en utilisant le ast paquet, en utilisant l' inspect , etc.).



4
votes

Il semble que le problème soit que python n'a aucune forme d'évaluation paresseuse

Euh ... oui, mais peut-être pas sous la forme que vous attendez. Les arguments de fonction SONT en effet évalués avant d'être passés à la fonction, donc

# XXX : python3 only, python2 doesn't accept
# keyword args after *args

def try_or(func, *exceptions, default=0):
    if not exceptions:
        exceptions = (Exception,)  
    try:
        return func()
    except exceptions as e:
        return default

# adding lists is legit too,
# so here you may want an empty list as the return value
# instead
a = [1, 2, 3]
# but only to lists
b = ""

result = try_or(lambda: a + b, TypeError, default=[]))

sera en effet exécuté comme:

def try_or_zero(func, *exceptions):
    if not exceptions:
        exceptions = (Exception,)  
    try:
        return func()
    except exceptions as e:
        return 0


a = 42
b = "c"

def add(x, y):
    return x + y

result = try_or_zero(lambda: add(a, b), TypeError))

Maintenant, les fonctions python sont des objets simples (elles peuvent être utilisées comme variables, transmises comme arguments aux fonctions, etc.), et elles ne sont invoquées que lors de l'application de l'opérateur d'appel (les parenthèses, avec ou sans arguments) afin que vous puissiez passer une fonction à try_or_zero et laissez try_or_zero appeler la fonction:

a = 42
b = "c"

def add(x, y):
    return x + y

result = try_or_zero(lambda: add(a, b))

Maintenant, vous allez objecter que 1 / cela ne fonctionnera pas si la fonction attend des arguments et 2 / avoir à écrire une fonction juste pour cela est un PITA - et les deux objections sont valides. Espérons que Python dispose également d'un raccourci pour créer des fonctions anonymes simples constituées d'une seule expression (même si elle est arbitrairement complexe): lambda . De plus, les fonctions python (y compris les «fonctions lambda» - qui sont, techniquement, des fonctions simples) sont fermées - elles capturent le contexte dans lequel elles sont définies - il est donc assez facile de regrouper tout cela:

def try_or_zero(func):
    try:
        return func()
    except Exception as e:
        return 0

Une note latérale sur la gestion des exceptions:

D'abord, n'utilisez pas d'exception nue, au moins attrapez l' Exception (sinon vous pourriez empêcher certaines exceptions - comme SysExit - de fonctionner comme prévu).

De préférence également, n'attrapez que les exceptions exactes que vous attendez à un moment donné. Dans votre cas, vous voudrez peut-être passer un tuple d'exceptions que vous souhaitez ignorer, c'est-à-dire:

param = foo.bar()
try_or_zero(param)

ce qui empêchera votre code de masquer les erreurs inattendues.

Et enfin: vous pouvez également ajouter la prise en charge d'une valeur de retour différente de zéro dans le cas d'une exception (toutes les expressions ne sont pas censées renvoyer un int):

try_or_zero(foo.bar())

0 commentaires