像在自己的呼叫者中一样调用函数?

时间:2013-04-01 05:02:13

标签: python debugging

我正在尝试复制我在Python中使用C编程时经常使用的错误检查模式。我有一个函数check如下:

def check(exceptions, msg, handler):
    def wrapped(func, *args, **kwargs):
        try:
            return func(*args, **kwargs)
        except exceptions as err:
            log_err(msg)
            # Do something with handler
    return wrapped

通过使用适当的参数调用check,然后使用函数及其参数调用结果,可以将try-except语句减少到两行代码而不会真正牺牲清晰度(在我的意见,无论如何)。

例如

def caller():
    try:
        files = listdir(directory)
    except OSError as err:
        log_err(msg)
        return []
    # Do something with files

变为

def caller():
    c = check(OSError, msg, handler)
    files = c(listdir, directory) # caller may return [] here
    # Do something with files

问题在于,为了使这个转换对程序的其余部分透明,handler必须执行,就好像它是在调用者范围内编写的一样wrapped。 (handler不一定是函数对象。我是在效果之后,而不是方法。)

在C中,我只是使用宏并扩展内联所有内容(因为这是我编写代码的地方),但Python没有宏。是否有可能以其他方式实现这种效果?

2 个答案:

答案 0 :(得分:1)

无法编写Python代码来创建对象c,因此,当您调用c时,调用它的函数将返回。您只能通过直接在该函数体中直接键入return语句(或从结尾处掉落)来从函数返回。

你可以很容易地做到这一点,以便你的"检查"函数只需返回默认值。然后呼叫者可以正常使用它,但它不能使呼叫者自己返回。您还可以为caller编写一个装饰器来捕获指定的错误并返回[],但这会捕获OSError中任何位置引发的所有caller,而不仅仅是listdir通过调用特定函数(例如,def check(exceptions, msg, handler): def deco(func): def wrapped(*args, **kwargs): try: return func(*args, **kwargs) except exceptions as err: print "Logged error:", msg return handler(err) return wrapped return deco def handler(err): return [] @check(ZeroDivisionError, "divide by zero", handler) def func(x): 1/0 >>> func(1) Logged error: divide by zero [] )。例如:

caller

你描述的情况似乎有点不寻常。在大多数情况下,将处理分解为"处理程序"是值得的。函数,处理函数要么不知道它希望调用者返回什么值,要么它可以根据错误知道返回什么,而不需要知道哪个特定行引发了错误。

例如,在您的示例中,您显然有一个函数OSError可能会在许多不同的点上引发OSError。如果您只有一个地方需要抓住[]并返回OSError,那么只需写一次尝试/除外,这没什么大不了的。如果你想在函数中捕获任何 []并返回OSError,请按照上面的说明进行装饰。您所描述的内容似乎仅在您想要捕获caller中提出的多于一个但不是全部可能{{1}}的情况下才有用,但在所有这些情况下您想要返回相同的特定值,这似乎很不寻常。

答案 1 :(得分:0)

如果我不太明白,请原谅我,但是你不能把处理程序放在调用函数中吗?

def caller():
    def handler():
        print("Handler was called")
    # ... 

更好的方法......

或者,如果您想简化调用方式,可以使用with语句来达到预期效果。我认为这将变得更加容易,并且它不是一个“黑客”:

class LogOSError(object):
    def __init__(self, loc):
        self.loc = loc
    def __enter__(self):
        return None
    def __exit__(self, exc_type, exc_value, traceback):
        if isinstance(exc_value, OSError):
            print("{}: OSError: {}".format(self.loc, exc_value))
            return True

你可以像这样使用它:

with LogOSError('example_func'):
    os.unlink('/does/not/exist')

输出是:

>>> with LogOSError('some_location'):
...     os.unlink('/does/not/exist')
... 
some_location: OSError: [Errno 2] No such file or directory: '/does/not/exist'