私は書いていない多くの深くネストされたjsonとやり取りし、pythonスクリプトを無効な入力に対してより「寛容」にしたいと考えています。ブロックし、むしろ疑わしい機能をラップするだけです。
例外を飲み込むことは悪いポリシーだと理解していますが、実際に実行を停止するよりも、後で印刷して分析することを希望します。私のユースケースでは、すべてのキーを取得するよりもループで実行を継続する方がより価値があります。
私が今やっていることは次のとおりです。
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'] = ''
私が望むものは次のとおりです(1):
item['a'] = f(myobject.get('key').get('subkey'))
item['b'] = f(myobject.get('key2'))
item['c'] = f(func1(myobject)
...
または(2):
@f
def get_stuff():
item={}
item['a'] = myobject.get('key').get('subkey')
item['b'] = myobject.get('key2')
item['c'] = func1(myobject)
...
return(item)
...ここで、単一のデータ項目(1)またはマスター関数(2)をラップできます。この関数では、実行停止例外を空のフィールドに変換し、stdoutに出力します。前者はアイテムごとのスキップのようなものです-そのキーが利用できず、ログに記録されて先に進みます-後者は行スキップです。フィールドのいずれかが機能しない場合、レコード全体がスキップしました。
私の理解では、何らかのラッパーがこれを修正できるはずです。ラッパーを使用して、次のことを試しました。
def f(func):
def silenceit():
try:
func(*args,**kwargs)
except:
print('Error')
return(silenceit)
動作しない理由は次のとおりです。存在しない関数を呼び出して、それをキャッチしません:
>>> f(meow())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'meow' is not defined
空白の戻り値を追加する前に、それを正しく取得して取得したいと思います。関数が機能していた場合、これは「エラー」を出力したでしょうか?
ここでラッパー関数は正しいアプローチですか?
[〜#〜] update [〜#〜]
以下に非常に便利で役立つ回答がたくさんありましたが、ありがとうございます。 mのtry-catchをラップする関数を具体的に探しています...
Defaultdictと Raymond HettingerのPyCon 2013プレゼンテーションで概説されているコンテキストマネージャアプローチ を使用できます。
from collections import defaultdict
from contextlib import contextmanager
@contextmanager
def ignored(*exceptions):
try:
yield
except exceptions:
pass
item = defaultdict(str)
obj = dict()
with ignored(Exception):
item['a'] = obj.get(2).get(3)
print item['a']
obj[2] = dict()
obj[2][3] = 4
with ignored(Exception):
item['a'] = obj.get(2).get(3)
print item['a']
ここには多くの良い答えがありますが、デコレータを介してこれを達成できるかどうかの質問に対処するものは見ませんでした。
短い答えは「いいえ」です。少なくとも、コードの構造を変更する必要があります。デコレータは、個々のステートメントではなく、機能レベルで動作します。したがって、デコレータを使用するには、装飾する各ステートメントを独自の関数に移動する必要があります。
ただし、装飾自体に割り当て自体を入れることはできないことに注意してください。装飾された関数からrhs式(割り当てられる値)を返し、外部で割り当てを行う必要があります。
これをサンプルコードの観点から言えば、次のパターンのコードを書くことができます。
@return_on_failure('')
def computeA():
item['a'] = myobject.get('key').METHOD_THAT_DOESNT_EXIST()
item["a"] = computeA()
return_on_failure
は次のようなものです。
def return_on_failure(value):
def decorate(f):
def applicator(*args, **kwargs):
try:
f(*args,**kwargs)
except:
print('Error')
return applicator
return decorate
構成可能なデコレータを使用して達成するのは非常に簡単です。
def get_decorator(errors=(Exception, ), default_value=''):
def decorator(func):
def new_func(*args, **kwargs):
try:
return func(*args, **kwargs)
except errors, e:
print "Got error! ", repr(e)
return default_value
return new_func
return decorator
f = get_decorator((KeyError, NameError), default_value='default')
a = {}
@f
def example1(a):
return a['b']
@f
def example2(a):
return doesnt_exist()
print example1(a)
print example2(a)
黙らせたいエラータイプと返されるデフォルト値をget_decoratorタプルに渡すだけです。出力は
Got error! KeyError('b',)
default
Got error! NameError("global name 'doesnt_exist' is not defined",)
default
編集: martineauのおかげで、エラーを防ぐために、エラーのデフォルト値を基本的な例外を含むタプルに変更しました。
あなたの場合、あなたは最初にニャーコール(存在しない)の値を評価し、それをデコレーターでラップします。これはそのようには機能しません。
最初に例外がラップされる前に発生し、次にラッパーが誤ってインデントされます(silenceit
はそれ自体を返すべきではありません)。あなたは次のようなことをしたいかもしれません:
def hardfail():
return meow() # meow doesn't exist
def f(func):
def wrapper():
try:
func()
except:
print 'error'
return wrapper
softfail =f(hardfail)
出力:
>>> softfail()
error
>>> hardfail()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in hardfail
NameError: global name 'meow' is not defined
とにかくあなたの場合、次のような単純な方法を使用しない理由がわかりません
def get_subkey(obj, key, subkey):
try:
return obj.get(key).get(subkey, '')
except AttributeError:
return ''
コード内:
item['a'] = get_subkey(myobject, 'key', 'subkey')
編集済み:
どんな深さでも機能するものが必要な場合に。次のようなことができます:
def get_from_object(obj, *keys):
try:
value = obj
for k in keys:
value = value.get(k)
return value
except AttributeError:
return ''
あなたが呼ぶこと:
>>> d = {1:{2:{3:{4:5}}}}
>>> get_from_object(d, 1, 2, 3, 4)
5
>>> get_from_object(d, 1, 2, 7)
''
>>> get_from_object(d, 1, 2, 3, 4, 5, 6, 7)
''
>>> get_from_object(d, 1, 2, 3)
{4: 5}
そしてあなたのコードを使用して
item['a'] = get_from_object(obj, 2, 3)
ところで、個人的な観点では、contextmanagerを使用した@cravooriソリューションも気に入っています。しかし、これは毎回3行のコードを持つことを意味します。
item['a'] = ''
with ignored(AttributeError):
item['a'] = obj.get(2).get(3)
予想される例外によって異なります。
唯一のユースケースがget()
の場合、次のようにします。
_item['b'] = myobject.get('key2', '')
_
それ以外の場合、デコレータのアプローチは有用かもしれませんが、あなたのやり方ではそうではありません。
見せようと思います:
_def f(func):
def silenceit(*args, **kwargs): # takes all kinds of arguments
try:
return func(*args, **kwargs) # returns func's result
except Exeption, e:
print('Error:', e)
return e # not the best way, maybe we'd better return None
# or a wrapper object containing e.
return silenceit # on the correct level
_
それでも、f(some_undefined_function())
は機能しません。
a)f()
は実行時にまだアクティブではなく、
b)間違って使用されている。正しい方法は、関数をラップしてから呼び出すことです:f(function_to_wrap)()
。
「ラムダの層」はここで役立ちます:
_wrapped_f = f(lambda: my_function())
_
存在しない関数を呼び出すラムダ関数をラップします。 wrapped_f()
を呼び出すと、ラッパーを呼び出します。ラッパーは、my_function()
を呼び出そうとするラムダを呼び出します。これが存在しない場合、ラムダはラッパーによってキャッチされる例外を発生させます。
これは、ラムダが定義されているときではなく、実行されているときに名前_my_function
_が実行されるために機能します。そして、この実行は、関数f()
によって保護およびラップされます。したがって、例外はラムダ内で発生し、デコレータによって提供されるラッピング関数に伝播され、デコレータはそれを適切に処理します。
ラムダ関数を次のようなラッパーで置き換えようとすると、ラムダ関数内へのこの動きは機能しません
_g = lambda function: lambda *a, **k: function(*a, **k)
_
その後に
_f(g(my_function))(arguments)
_
ここでは名前解決が「表面に戻る」ためです。_my_function
_は解決できず、これはg()
またはf()
が呼び出される前に発生します。だからそれは動作しません。
そして、あなたが次のようなことをしようとすると
_g(print)(x.get('fail'))
_
g()
はx
ではなくprint
を保護するため、x
がない場合は機能しません。
ここでx
を保護したい場合、あなたはする必要があります
_value = f(lambda: x.get('fail'))
_
f()
によって提供されるラッパーは、そのラムダ関数を呼び出して例外を発生させ、その後、それを黙らせるからです。
たくさんの壊れたコードを扱っているので、この場合eval
を使用することは許されるかもしれません。
def my_eval(code):
try:
return eval(code)
except: # Can catch more specific exceptions here.
return ''
次に、破損している可能性のあるすべてのステートメントをラップします。
item['a'] = my_eval("""myobject.get('key').get('subkey')""")
item['b'] = my_eval("""myobject.get('key2')""")
item['c'] = my_eval("""func1(myobject)""")
@iruvar回答の拡張-Python 3.4で始まるPython standard lib: https:// docs。 python.org/3/library/contextlib.html#contextlib.suppress
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('somefile.tmp')
with suppress(FileNotFoundError):
os.remove('someotherfile.tmp')
なぜサイクルを使用しないのですか?
for dst_key, src_key in (('a', 'key'), ('b', 'key2')):
try:
item[dst_key] = myobject.get(src_key).get('subkey')
except Exception: # or KeyError?
item[dst_key] = ''
または、小さなヘルパーを作成したい場合:
def get_value(obj, key):
try:
return obj.get(key).get('subkey')
except Exception:
return ''
また、値を取得する必要がある場所がいくつかあり、ヘルパー機能がより合理的である場合は、両方のソリューションを組み合わせることができます。
あなたが実際にあなたの問題のためにデコレータが必要かどうかはわかりません。