【问题标题】:Decorating a module on import without affecting functions implemented in terms of other functions在导入时装饰模块而不影响根据其他功能实现的功能
【发布时间】:2014-04-23 04:50:35
【问题描述】:

我正在使用一个接口来声明几种验证方法。为简单起见,我们可以考虑三种方法。 verify_less_than()verify_equal()verify_less_than_equal()。 (参见下面的实现)。当我导入这些时,我想装饰它们,以便如果验证失败 - verify_less_than( 10, 5 ) - 将引发异常。

我已经为前两个函数工作了,但第三个函数给了我一个更难的时间。因为verify_less_than_equal是根据前两个方法定义的,如果第一个调用失败 - verify_less_than_equal( 5, 5 ) - 在调用第二个之前会抛出异常。

对此的任何帮助将不胜感激

示例代码:

模块.py

​​>
class needs_decoration():
    def verify_less_than( self, x, y ):
        return( x < y )

    def verify_equal( self, x, y ):
        return( x == y )

    def verify_less_than_equal( self, x, y ):
        return( self.verify_less_than( x, y ) or self.verify_equal( x, y ) )

implementation.py

​​>
import types
import module

def decorate( fn ):
    def wrapped( self, x, y ):
        res = fn( self, x, y )
        if res == False:
            raise Exception( 'Verification failed!' )
        return res
    return wrapped

for k, v in vars( module.needs_decoration ).items():
    if isinstance( v, types.FunctionType ):
        if not '__init__' in str( vars( module.needs_decoration )[ k ] ):
            vars( module.needs_decoration )[ k ] = decorate( v )

verifier = module.needs_decoration()
verifier.verify_less_than_equal( 5, 5 ) # This will raise an exception, and I would like it not to

【问题讨论】:

  • 您确定要使用异常而不是布尔值吗?
  • 如果不深入了解方法的实现方式或查找堆栈以查看您当前是否在另一个修饰调用中,您将无法做您想做的事情。这很快就会变得丑陋。
  • @joel 我绝对是。这有望用于自动化测试设置。因此,当遇到异常时,控制将返回到高级控制器,在那里它可以决定是否应该重新运行失败的测试部分。使用异常使控件更清晰(必须包装每个验证调用 - 数百个 - 只会使测试混乱)。
  • 这似乎更容易导致错误而不是修复它们。如果您的代码依赖于验证抛出这些错误怎么办?
  • @user2357112 如果我们的验证步骤之一失败,我们的测试将被视为失败,因此验证失败后发生的任何事情都无关紧要。通过抛出异常,我们绕过等待直到测试结束(几个小时后),并且可以立即评估情况。

标签: python wrapper decorator


【解决方案1】:

您可以定义私有(带下划线)的方法,这些方法将保持未修饰,以便_verify_less_than_equal 可以调用未修饰的函数。 for-loop 然后可以添加公共 API(不带下划线的方法),它们是私有方法的修饰版本:

import types

def add_decorators(cls):
    def decorate(fn):
        def wrapped(self, x, y):
            res = fn(self, x, y)
            if not res:
                raise ValueError('Verification failed! {}({}, {}) is False'
                                .format(fn.__name__, x, y))
            return res
        return wrapped

    for k, v in vars(cls).items():
        if isinstance(v, types.FunctionType):
            if k.startswith('__'): continue
            if k.startswith('_'):
                setattr(cls, k[1:], decorate(v))
    return cls

@add_decorators
class NeedsDecoration():
    def _verify_less_than(self, x, y):
        return x < y

    def _verify_equal(self, x, y):
        return x == y

    def _verify_less_than_equal(self, x, y):
        return self._verify_less_than(x, y) or self._verify_equal(x, y)

    def __init__(self): pass
verifier = NeedsDecoration()
assert verifier.verify_less_than_equal(5, 5)

根据需要,最后一行不会引发异常。

【讨论】:

  • 这当然是可行的,但必须使用不同的方法名称并不理想。
猜你喜欢
  • 2017-03-13
  • 1970-01-01
  • 1970-01-01
  • 2012-07-02
  • 2022-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-10
相关资源
最近更新 更多