【问题标题】:Using a decorator to add return the inner function with a property added使用装饰器添加返回带有属性的内部函数
【发布时间】:2013-01-20 11:32:32
【问题描述】:

在尝试保存对象时,我有大量作为测试运行的函数,因此我创建了一种方法来更方便地将这些方法注册为对象的约束(测试)。

我对在我的解决方案中使用装饰器感到困惑。我打算做什么:

  • 创建一个装饰器,为对象中的方法添加属性
  • 装饰我想要标记为约束的方法。
  • 循环我的对象上的所有方法并调用以执行所有具有标志的方法

使用装饰器不起作用,但我自己添加标志可以。

代码如下:

from functools import wrap

def constraint(func):
    @wraps(func)
    def inner(*args, **kwargs):
        func._is_constraint = True  # Here after returning the function I add a flag
        return func(*args, **kwargs)
    return inner

class Record(object):
    param1 = 100  # some defaults
    param2 = 20

    @constraint  # This dont works
    def check_test1(self):
        if param1 < 0:
            return 'No value less than zero'

    # This works fine
    def check_test2(self):
        if param2 < 0:
            return 'No value less than zero'
    check_test2._is_constraint = True

    def a_random_method(self):
        print 'some random thing'

所以我正在尝试:

>>> rec = Record()
>>> rec.param1 = -100
>>> rec.param2 = -100
>>> for prop in dir(rec):
...     if hasattr(getattr(rec, prop), '_is_constraint'):
...         constraint = getattr(rec, prop)
...             print prop, constraint()
...    
'check_param2: No value less than zero'
>>>

看到 check_param1 没有被执行。

那么,我怎样才能使用装饰器让它工作呢?有可能吗?

我正在尝试在这种特殊情况下使用https://gist.github.com/mariocesar/4684561

【问题讨论】:

    标签: python python-decorators


    【解决方案1】:

    嗯,我看到的第一件事是,你想将参数添加到装饰器中的函数对象,而不是闭包;当从装饰范围调用函数时,会调用闭包,因此在闭包中对其进行标记是事后进行的:

    def constraint(func):
        func._is_constraint = True
        @wraps(func)
        def inner(*args, **kwargs):
            return func(*args, **kwargs)
        return inner
    

    另外,如果你只是在闭包中直接调用 func 而不添加任何功能,只是标记它,那么闭包是完全多余的:

     def constraint(func):
        func._is_constraint = True
        return func
    

    在这种情况下,不同的模式可能会更好地为您服务:

    class ConstraintsType(type):
    
        def __new__(cls, name, bases, attrs):
    
            attrs['constraint_names'] = []
            for attr in attrs:
                if attr.startswith('constraint_'):
                    attrs['constraint_names'].append(attr)
            return super(ConstraintsType, cls).__new__(cls, name, bases, attrs)
    
    class Constraints(object):
    
        __metaclass__ = ConstraintsType
    
        @property
        def constraints(self):
    
            for name in self.constraint_names:
                yield getattr(self, name)
    
    class Record(Constraints):
    
        def __init__(self, params=(100, 20)):
    
            self.params = params
    
        def constraint_greater_than_0(self, value):
    
            return value > 0
    
       def run(self):
    
           for index, value in enumerate(self.params):
               for func in self.constraints:
                   if not func(value):
                       print 'param %d (%s) did not satisfy constraint %s' % (index, value, func.__name__)
    
    Record().run()
    for value_set in ((-100, -100), (0, 0), (-1,1), (1,-1)):
        Record(value_set).run()
    

    【讨论】:

      猜你喜欢
      • 2021-08-21
      • 1970-01-01
      • 2017-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-25
      • 2020-04-28
      • 2014-05-19
      相关资源
      最近更新 更多