【问题标题】:Python: decorator/wrapper for try/except statementPython:try/except 语句的装饰器/包装器
【发布时间】:2017-02-18 19:08:24
【问题描述】:

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

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: 生成器没有产生

【问题讨论】:

  • 你不应该将事情与真实进行比较;这是不必要的多余。将 `if config.DEVELOPMENT == True` 更改为 if config.DEVELOPMENT
  • 为什么要传入一个字符串?只需传入值本身:with try_dev_config(config.DEVELOPMENT):?访问该属性有问题吗?
  • @MartijnPieters,谢谢with try_dev_config(config.DEVELOPMENT): 工作很好,但如果 config.DEVELOPMENT 是 False 我又得到 RuntimeError: generator didn't yield
  • 对,是的,上下文管理器不是有条件地运行块的地方。不要将if 放在上下文管理器中,将其放在外面。
  • @MartijnPieters,如果 config.DEVELOPMENT == True,我该如何编写运行任何代码块的函数?

标签: python try-catch wrapper decorator


【解决方案1】:

你可以传入一个函数。

boolean = True

def pass_this_in():
    print("I just did some stuff")

def the_try_except_bit(function):
    try:
        if boolean:
            function()
    except:
        print("Excepted")


# Calling the above code
the_try_except_bit(pass_this_in)

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

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

【讨论】:

    【解决方案2】:

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

    恕我直言,您需要的是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 结构内被调用,并且仅当 config.DEVELOPMENT 被评估为 True 时。如有异常,logger 用于记录信息。
    • 要装饰的每个代码块都放入一个函数(do_stuff_1do_stuff_2 并使用 check_dev_config 装饰器生成器进行装饰,并为其指定 configlogger
    • 当调用装饰函数时,它们是通过装饰器而不是直接调用的。如您所见,do_stuff_2 异常已被捕获并记录了一条消息。

    【讨论】:

      猜你喜欢
      • 2017-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-20
      • 1970-01-01
      • 1970-01-01
      • 2011-06-25
      • 1970-01-01
      相关资源
      最近更新 更多