web-dev-qa-db-ja.com

Pythonを除いて試してみる一般的なデコレータ?

私は書いていない多くの深くネストされた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をラップする関数を具体的に探しています...

  1. メソッドが存在しない場合。
  2. オブジェクトが存在せず、呼び出されたメソッドを取得しているとき。
  3. 存在しないオブジェクトが関数の引数として呼び出されているとき。
  4. これらのいずれかの組み合わせ。
  5. ボーナス、機能が存在しない場合。
40
Mittenchops

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']
33
iruvar

ここには多くの良い答えがありますが、デコレータを介してこれを達成できるかどうかの質問に対処するものは見ませんでした。

短い答えは「いいえ」です。少なくとも、コードの構造を変更する必要があります。デコレータは、個々のステートメントではなく、機能レベルで動作します。したがって、デコレータを使用するには、装飾する各ステートメントを独自の関数に移動する必要があります。

ただし、装飾自体に割り当て自体を入れることはできないことに注意してください。装飾された関数から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
20
Nathan Davis

構成可能なデコレータを使用して達成するのは非常に簡単です。

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のおかげで、エラーを防ぐために、エラーのデフォルト値を基本的な例外を含むタプルに変更しました。

20
Sergeev Andrew

あなたの場合、あなたは最初にニャーコール(存在しない)の値を評価し、それをデコレーターでラップします。これはそのようには機能しません。

最初に例外がラップされる前に発生し、次にラッパーが誤ってインデントされます(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) 
8
astreal

予想される例外によって異なります。

唯一のユースケースが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()によって提供されるラッパーは、そのラムダ関数を呼び出して例外を発生させ、その後、それを黙らせるからです。

8
glglgl

たくさんの壊れたコードを扱っているので、この場合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)""")
5
Addison

@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')
4
Pax0r

なぜサイクルを使用しないのですか?

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

また、値を取得する必要がある場所がいくつかあり、ヘルパー機能がより合理的である場合は、両方のソリューションを組み合わせることができます。

あなたが実際にあなたの問題のためにデコレータが必要かどうかはわかりません。

2
simplylizz