Général décorateur pour envelopper essayer sauf en python?
J'avais l'interaction avec beaucoup de profondément imbriqués json je n'ai pas écrit, et que vous souhaitez faire mon script python plus de "pardonner" à une entrée non valide. Je me retrouve à écrire tentent-à l'exception des blocs, et serait plutôt juste envelopper le douteux fonction.
Je comprends que c'est une mauvaise politique d'avaler des exceptions, mais je préfère préfèrent-ils être imprimés et analysés plus tard, que pour réellement arrêter l'exécution. Il est plus intéressant, dans mon cas d'utilisation de poursuivre l'exécution sur la boucle que pour obtenir toutes les clés.
Voici ce que je fais aujourd'hui:
try:
item['a'] = myobject.get('key').METHOD_THAT_DOESNT_EXIST()
except:
item['a'] = ''
try:
item['b'] = OBJECT_THAT_DOESNT_EXIST.get('key2')
except:
item['b'] = ''
try:
item['c'] = func1(ARGUMENT_THAT_DOESNT_EXIST)
except:
item['c'] = ''
...
try:
item['z'] = FUNCTION_THAT_DOESNT_EXIST(myobject.method())
except:
item['z'] = ''
Voici ce que j'aimerais, (1):
item['a'] = f(myobject.get('key').get('subkey'))
item['b'] = f(myobject.get('key2'))
item['c'] = f(func1(myobject)
...
ou (2):
@f
def get_stuff():
item={}
item['a'] = myobject.get('key').get('subkey')
item['b'] = myobject.get('key2')
item['c'] = func1(myobject)
...
return(item)
...où je peux enrouler le seul élément de données (1), ou une fonction de maître (2), dans une fonction qui transforme l'exécution de l'arrêt des exceptions dans des champs vides, imprimé à stdout. L'ancien serait une sorte de point-sage skip - où que la clé n'est pas disponible, il enregistre le vide et se déplace sur - le dernier est une ligne sauter, où si un des champs n'est pas le travail, l'ensemble du dossier est ignorée.
Ma compréhension est que de l'emballage doit être en mesure de résoudre ce problème. Voici ce que j'ai essayé, avec un wrapper:
def f(func):
def silenceit():
try:
func(*args,**kwargs)
except:
print('Error')
return(silenceit)
Voici pourquoi il ne fonctionne pas. Appeler une fonction qui n'existe pas, il n'a pas essayer de l'attraper à l'écart:
>>> f(meow())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'meow' is not defined
Avant que je même ajouter un vide valeur de retour, j'aimerais obtenir à essayer de l'attraper correctement. Si la fonction avait travaillé, cela aurait imprimé "Erreur", pas vrai?
Est une fonction wrapper de l'approche correcte ici?
Mise à JOUR
J'ai eu beaucoup de vraiment utile réponses ci-dessous, et vous en remercions---mais j'ai édité les exemples que j'ai utilisé ci-dessus pour illustrer le fait que je suis en train d'essayer d'attraper plus de clé imbriquée des erreurs, que je suis à la recherche spécifiquement pour une fonction qui encapsule un try-catch pour...
- Lorsqu'une méthode n'existe pas.
- Lorsqu'un objet n'existe pas, et est de trouver une méthode appelée sur elle.
- Lorsqu'un objet qui n'existe pas est appelé comme argument à une fonction.
- Toute combinaison de l'un quelconque de ces choses.
- Bonus, lorsqu'une fonction n'existe pas.
- Pour accéder imbriquée JSON plus précisément, vous pouvez jeter un oeil à safeJSON. Cela fonctionne en enveloppant l'objet
myobject
.
Vous devez vous connecter pour publier un commentaire.
Vous pouvez utiliser un defaultdict et le gestionnaire de contexte comme indiqué dans Raymond Hettinger de PyCon 2013 présentation
Il y a beaucoup de bonnes réponses ici, mais je ne vois pas que répondre à la question de savoir si vous pouvez accomplir cela par les décorateurs.
La réponse est "non", au moins pas sans changements structurels à votre code. Les décorateurs de fonctionner au niveau de la fonction, et non pas sur les déclarations individuelles. Par conséquent, afin d'utiliser les décorateurs, vous devez déplacer chacun des états à être décorées dans sa propre fonction.
Mais notez que vous ne pouvez pas simplement mettre l'affectation à l'intérieur de lui-même décoré de la fonction. Vous devez retourner le membre de droite de l'expression (la valeur à attribuer) de l'décoré de la fonction, puis de le refaire à l'extérieur.
Pour mettre cela en termes de votre code d'exemple, on peut écrire du code avec le motif suivant:
return_on_failure
pourrait être quelque chose comme:Il est très facile à réaliser en utilisant configurable décorateur.
Juste passer get_decorator les tuples avec les types d'erreur qui vous voulez le silence et la valeur par défaut pour le retour.
La sortie sera
Edit: Grâce à martineau j'ai changé la valeur par défaut des erreurs de tuples avec une base d'Exception pour éviter les erreurs.
@f
à une fonction qui renvoie la valeur de lasoup.find("span", class_='xxx').text
expression de production de l'exception -- comme illustré dans les exemples de la réponse.get_decorator()
àdef get_decorator(default_value='', *errors)
pour simplifier l'appel légèrement.@get_decorator((KeyError, NameError), default_value='default')
avant que la fonction serait aussi travailler.Cela dépend de ce que les exceptions que vous attendez.
Si votre seul cas d'utilisation est
get()
, vous pourriez fairePour les autres cas, votre décorateur d'approche pourrait être utile, mais pas dans la façon dont vous le faites.
Je vais essayer de vous montrer:
Néanmoins,
f(some_undefined_function())
ne fonctionne pas, parce quea)
f()
n'est pas encore active à la executon temps etb) il est utilisé à tort. La bonne façon serait d'intégrer la fonction et ensuite l'appeler:
f(function_to_wrap)()
.Une "couche de lambda" serait de l'aide ici:
enroule une fonction lambda qui à son tour appelle une fonction inexistante. L'appel de
wrapped_f()
conduit à l'appel de la surcouche qui appelle le lambda qui tente de l'appelermy_function()
. Si cela n'existe pas, le lambda soulève une exception qui est capturé par le wrapper.Cela fonctionne parce que le nom
my_function
n'est pas exécutée au moment de la lambda est défini, mais quand il est exécuté. Et cette exécution est protégé et emballé par la fonctionf()
ensuite. Si l'exception se produit à l'intérieur de la lambda et est propagée à l'emballage de la fonction fournie par le décorateur, qui gère gracieusement.Ce mouvement vers l'intérieur de la fonction lambda ne fonctionnent pas si vous essayez de remplacer la fonction lambda avec un wrapper comme
suivie par un
car ici, la résolution de nom est "de retour à la surface":
my_function
ne peut pas être résolu et ce qui se passe avantg()
ou mêmef()
sont appelés. Si cela ne fonctionne pas.Et si vous essayez de faire quelque chose comme
ça ne peut pas fonctionner si vous n'avez pas de
x
, parce queg()
protègeprint
, pasx
.Si vous voulez protéger
x
ici, vous aurez à faireparce que l'emballage fourni par
f()
appels de fonction lambda qui soulève une exception qui est ensuite réduit au silence.>>> wrapped_p = f(lambda *z,**z: func(*z,**z))
et a obtenuFile "<stdin>", line 1 SyntaxError: duplicate argument 'z' in function definition
g = lambda function: lambda *a, **k: function(*a, **k)
qui vous permet de faire desg(anyfunction)()
. (Uniquement de cette façon: vous ne voulez pas envelopper le résultat de la fonction, mais la fonction elle-même.)z
deux fois, il est clair que vous obtenez un message d'erreur...>>> g = f(lambda function: lambda *a, **k: function(*a,**k))
Puis-je appeler avec>>> g(print)(x.get('fail'))
et obtenez un message indiquant que x n'existe pas, plutôt que le silence de l'échec, je suis à la recherche pour.dans votre cas, vous d'abord évaluer la valeur de la miaou appel (qui n'existe pas) et puis l'envelopper dans le décorateur. cela ne fonctionne pas de cette façon.
d'abord l'exception est soulevée devant il était enveloppé, puis le wrapper est mal indenté (
silenceit
ne doit pas renvoyer de lui-même). Vous voulez peut-être faire quelque chose comme:de sortie:
de toute façon, dans votre cas, je ne comprends pas pourquoi vous n'utilisez pas une méthode simple comme
et dans le code:
Édité:
Dans le cas où vous voulez quelque chose qui va travailler à n'importe quelle profondeur. Vous pouvez faire quelque chose comme:
Que vous souhaitez appeler:
Et à l'aide de votre code
Par la voie, sur un point de vue personnel j'aime aussi @cravoori solution à l'aide de contextmanager. Mais cela voudrait dire avoir trois lignes de code à chaque fois:
Puisque vous avez à traiter avec beaucoup de code cassé, il peut être justifiable d'utiliser
eval
dans ce cas.Puis les envelopper tous vos potentiellement cassé états:
L'extension @iruvar réponse - débuter avec Python 3.4 il existe un gestionnaire de contexte pour ce en Python standard lib: https://docs.python.org/3/library/contextlib.html#contextlib.suppress
Pourquoi ne pas simplement utiliser le cycle?
Ou si vous souhaitez écrire une petite aide:
Vous pouvez aussi combiner les deux solutions si vous avez quelques endroits où vous avez besoin pour obtenir la valeur et la fonction d'assistance serait plus raisonnable.
Pas sûr que vous avez réellement besoin d'un décorateur pour votre problème.