除python外,是否尝试使用常规装饰器包装?
我曾经与很多我没有编写的嵌套很深的json进行过交互,我想让我的python脚本对无效输入更加“宽容”。我发现自己在编写try-except-blocks时遇到了麻烦,我宁愿把这个可疑的函数包装起来 我理解接受异常是一种糟糕的策略,但我更希望它们在以后打印和分析,而不是实际停止执行。在我的用例中,在循环中继续执行比获取所有键更有价值 以下是我现在正在做的:除python外,是否尝试使用常规装饰器包装?,python,try-catch,wrapper,decorator,Python,Try Catch,Wrapper,Decorator,我曾经与很多我没有编写的嵌套很深的json进行过交互,我想让我的python脚本对无效输入更加“宽容”。我发现自己在编写try-except-blocks时遇到了麻烦,我宁愿把这个可疑的函数包装起来 我理解接受异常是一种糟糕的策略,但我更希望它们在以后打印和分析,而不是实际停止执行。在我的用例中,在循环中继续执行比获取所有键更有价值 以下是我现在正在做的: try: item['a'] = myobject.get('key').METHOD_THAT_DOESNT_EXIST() ex
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):
或(二):
…在这里,我可以将单个数据项(1)或主函数(2)包装到某个函数中,该函数将停止执行的异常转换为空字段,并打印到标准输出。前者是一种逐项跳转——在该键不可用的情况下,它记录为空白并继续移动——后者是一种行跳转,如果任何字段不起作用,则跳过整个记录
我的理解是某种包装应该能够解决这个问题。下面是我用包装纸试过的:
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
>>f(喵喵())
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
名称错误:未定义名称“喵喵”
在我添加一个空白返回值之前,我想让它正确地尝试捕获。如果这个函数起作用,它会打印“错误”,对吗
包装器函数是正确的方法吗
更新
下面我得到了很多非常有用、有用的答案,谢谢你们——但我编辑了上面使用的示例,以说明我正在尝试捕获的不仅仅是嵌套的键错误,我正在寻找一个函数,它包装了一个try-catch for
这取决于您预期的例外情况 如果您唯一的用例是
get()
,您可以这样做
item['b'] = myobject.get('key2', '')
对于其他情况,您的decorator方法可能很有用,但不是以您这样做的方式
我将尝试向您展示:
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(一些未定义的函数())
将无法工作,因为
a) f()
在执行时间和
b) 它用错了。正确的方法是包装函数,然后调用它:f(function-to-wrap)(
)
“lambda层”有助于:
wrapped_f = f(lambda: my_function())
包装一个lambda函数,该函数反过来调用一个不存在的函数。调用wrapped\u f()
会导致调用lambda的包装器,lambda试图调用my\u函数()
。如果不存在,lambda将引发一个异常,该异常被包装器捕获
这是因为名称my_函数
不是在定义lambda时执行的,而是在执行lambda时执行的。然后,该执行由函数f()
保护和包装。因此,异常发生在lambda内部,并传播到decorator提供的包装函数中,该函数将优雅地处理异常
如果您试图用类似于的包装器替换lambda函数,那么这种向lambda函数内部移动的做法是行不通的
g = lambda function: lambda *a, **k: function(*a, **k)
接着是
f(g(my_function))(arguments)
因为这里的名称解析是“返回到表面”:my_函数
无法解析,这发生在调用g()
甚至f()
之前。所以它不起作用
如果你想做类似的事情
g(print)(x.get('fail'))
如果没有x
,它就不能正常工作,因为g()
保护打印,而不是x
如果您想在此处保护x
,您必须这样做
value = f(lambda: x.get('fail'))
因为f()
提供的包装器调用lambda函数,该函数会引发一个异常,该异常随后被静默。在您的例子中,您首先计算meow调用的值(该值不存在),然后将其包装到装饰器中。这样不行
首先,在包装异常之前引发异常,然后包装错误缩进(sileneit
不应返回自身)。您可能希望执行以下操作:
def hardfail():
return meow() # meow doesn't exist
def f(func):
def wrapper():
try:
func()
except:
print 'error'
return wrapper
softfail =f(hardfail)
def get_from_object(obj, *keys):
try:
value = obj
for k in keys:
value = value.get(k)
return value
except AttributeError:
return ''
def return_on_failure(value):
def decorate(f):
def applicator(*args, **kwargs):
try:
f(*args,**kwargs)
except:
print('Error')
return applicator
return decorate
输出:
>>> 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
在守则中:
item['a'] = get_subkey(myobject, 'key', 'subkey')
编辑:
以防你想要在任何深度都能工作的东西。您可以执行以下操作:
def hardfail():
return meow() # meow doesn't exist
def f(func):
def wrapper():
try:
func()
except:
print 'error'
return wrapper
softfail =f(hardfail)
def get_from_object(obj, *keys):
try:
value = obj
for k in keys:
value = value.get(k)
return value
except AttributeError:
return ''
def return_on_failure(value):
def decorate(f):
def applicator(*args, **kwargs):
try:
f(*args,**kwargs)
except:
print('Error')
return applicator
return decorate
你会称之为:
>>> 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解决方案。但这意味着每次有三行代码:
item['a'] = ''
with ignored(AttributeError):
item['a'] = obj.get(2).get(3)
为什么不直接使用循环呢
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 ''
另外,如果您有几个地方需要获取值,那么您可以将这两种解决方案结合起来,并且帮助函数将更合理
不确定您是否真的需要一个装饰程序来解决您的问题。您可以使用defaultdict和
使用可配置的装饰器很容易实现
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)
只需传递以获取包含错误类型的_decorator元组,您希望该错误类型为静默,并返回默认值。
输出将是
Got error! KeyError('b',)
default
Got error! NameError("global name 'doesnt_exist' is not defined",)
default
编辑:多亏了martineau,我将错误的默认值更改为元组,并添加了基本异常以防止错误。由于您要处理大量损坏的代码,在这种情况下使用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)""")
这里有很多很好的答案,但是我没有看到任何关于你是否可以通过装饰来完成这一点的答案
简短的回答是“不”,至少在没有对代码进行结构性修改的情况下是这样。修饰符在函数级别操作,而不是在单个语句上。因此,为了使用decoro
@return_on_failure('')
def computeA():
item['a'] = myobject.get('key').METHOD_THAT_DOESNT_EXIST()
item["a"] = computeA()
def return_on_failure(value):
def decorate(f):
def applicator(*args, **kwargs):
try:
f(*args,**kwargs)
except:
print('Error')
return applicator
return decorate
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('somefile.tmp')
with suppress(FileNotFoundError):
os.remove('someotherfile.tmp')