Использование декоратора для возврата внутренней функции с добавленным свойством

У меня есть большое количество функций, которые запускаются как тесты при попытке сохранить объект, поэтому я создаю способ, чтобы было удобнее регистрировать эти методы как ограничения (тесты) для объекта.

Я запутался в использовании декораторов в своем решении. Что я собирался сделать:

  • Создайте декоратор, который добавляет свойство к методу в объекте
  • Украсьте методы, которые я хочу пометить как ограничения.
  • Зациклить все методы на моем объекте и вызвать для выполнения все, что имеет флаг

Использование декоратора не работает, но добавление флага само по себе работает.

Вот код:

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


person Mario César    schedule 05.02.2013    source источник


Ответы (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()
person Silas Ray    schedule 05.02.2013