Python:try / except语句的装饰器/包装器

时间:2016-10-10 11:12:26

标签: python try-catch wrapper decorator

我有一些需要用函数包装的代码块。

try:
    if config.DEVELOPMENT == True:
        # do_some_stuff
except:
    logger.info("Config is not set for development")

然后我会再做一次:

try:
    if config.DEVELOPMENT == True:
        # do_some_another_stuff
except:
    logger.info("Config is not set for development")

那么,如何包装这个“do_some_stuff”和“do_some_another_stuff”?

我正在尝试使用contextmanager编写函数:

@contextmanager
def try_dev_config(name):
    try:
        if name is not None:
            yield
    except Exception as e:
        print "not dev config"

with try_dev_config("config.DEVELOPMENT"):
    # do_some_stuff

我收到了一个错误:

  

RuntimeError:生成器没有产生

2 个答案:

答案 0 :(得分:0)

你可以传递一个函数。

pass_this_in = lambda : print("I just did some stuff")

如果要减少“pass_this_in”定义位,则可以使用lambda function definitions

$ gcc -g -W -Wall -Wextra x.c -o x
x.c: In function 'append':
x.c:56:5: warning: format '%d' expects argument of type 'int *', but argument 6 has type 'int' [-Wformat=]
     int r = sscanf(input, "%s%s%s%d", command, fName, lName, score);
     ^
x.c:56:9: warning: 'score' is used uninitialized in this function [-Wuninitialized]
     int r = sscanf(input, "%s%s%s%d", command, fName, lName, score);
         ^
$ valgrind ./x test.txt 
==16814== Memcheck, a memory error detector
==16814== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==16814== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==16814== Command: ./x test.txt
==16814== 
a b c 123
==16814== Use of uninitialised value of size 8
==16814==    at 0x4E9B2D9: _IO_vfscanf (in /lib64/libc-2.20.so)
==16814==    by 0x4EA9BAB: __isoc99_vsscanf (in /lib64/libc-2.20.so)
==16814==    by 0x4EA9B26: __isoc99_sscanf (in /lib64/libc-2.20.so)
==16814==    by 0x400A4A: append (x.c:56)
==16814==    by 0x400993: runCommand (x.c:39)
==16814==    by 0x4008F9: main (x.c:26)

答案 1 :(得分:0)

我不确定上下文管理器是实现您想要的好方法。上下文管理器的目标是提供一种机制来打开/实例化资源,提供(或不访问)资源,并在您不再需要时自动关闭/清理它。

恕我直言,你需要的是decorator。 装饰器旨在围绕函数调用执行代码。它会强迫你将每个代码块放在一个函数中,但我不认为它是如此困难。您可以像这样实现它:

class Config(object):
    """for demonstration purpose only: used to have a config.DEVELOPMENT value"""
    DEVELOPMENT = True

class Logger(object):
    """for demonstration purpose only: used to have a logger.info method"""
    @staticmethod
    def info(msg):
        print("Logged: {}".format(msg))

def check_dev_config(config, logger):
    def dev_config_checker(func):
        def wrapper(*args, **kwargs):
            try:
                if config.DEVELOPMENT:
                    func(*args, **kwargs)
            except Exception as err:
                logger.info(
                    "Config is not set for developpement: {}".format(err))
        return wrapper
    return dev_config_checker

@check_dev_config(Config, Logger)
def do_stuff_1():
    print("stuff 1 done")

@check_dev_config(Config, Logger)
def do_stuff_2():
    raise Exception("stuff 2 failed")

do_stuff_1()
do_stuff_2()

此代码打印

stuff 1 done
Logged: Config is not set for developpement: stuff 2 failed

说明:

  • check_dev_config函数实际上是一个装饰器生成器,它接受configlogger作为参数。
  • 它返回dev_config_checker函数,它是一个实际的(和参数化的)装饰器,它接受一个函数作为参数进行修饰。
  • 这个装饰器返回一个wrapper函数,它实际上会在装饰函数调用周围运行代码。在此函数中,仅在try/except被评估为True时才在config.DEVELOPMENT结构内调用修饰函数。如果发生异常,logger用于记录信息。
  • 要装饰的每个代码块都放入一个函数(do_stuff_1do_stuff_2并用check_dev_config装饰器生成器修饰,为其赋予config和{{ 1}}。
  • 当调用修饰函数时,它们通过装饰器而不是直接调用。如您所见,已捕获logger异常,并且已记录消息。