如何在装饰器中设置实例变量?

时间:2013-06-14 19:39:03

标签: python python-decorators

我有一个计算工资成分的课程,如下所示。

def normalize(func):
    from functools import wraps

    @wraps(func)
    def wrapper(instance, *args, **kwargs):
        allowanceToCheck = func(instance)
        if instance.remainingAmount <= 0:
            allowanceToCheck = 0.0
        elif allowanceToCheck > instance.remainingAmount:
            allowanceToCheck = instance.remainingAmount
        instance.remainingAmount = instance.remainingAmount - allowanceToCheck
        return allowanceToCheck
    return wrapper

class SalaryBreakUpRule(object):
    grossPay = 0.0
    remainingAmount = 0.0

    @property
    def basic(self):
     # calculates the basic pay according to predefined salary slabs.
     basic = 6600 # Defaulting to 6600 for now.
     self.remainingAmount = self.grossPay - basic
     return basic

    @property
    @normalize
    def dearnessAllowance(self):
      return self.basic * 0.2

    @property
    @normalize
    def houseRentAllowance(self):
      return self.basic * 0.4

   def calculateBreakUps(self, value = 0.0):
    self.grossPay = value
    return {
        'basic' : self.basic,
        'da' : self.dearnessAllowance,
        'hra' : self.houseRentAllowance
    }

在计算每项津贴之前,我需要检查所有津贴的总和是否不超过grossPay,即我的总薪水。我写了一个装饰器,它包装了每个容差计算方法并完成了上述要求。例如,

* an employee having a salary of Rs.6700
* basic = 6,600 (according to slab)
* dearnessAllowance = 100 (cos 20% of basic is more than remaining amount)
* houseRentAllowance = 0.0 (cos 40% of basic is more than remaining amount)

但不幸的是它没有用。第一个补贴是正确计算的,但所有其他补贴的价值与第一个补贴的价值相同。即如上所述,houseRentAllowance将有100而不是0.0。

我发现的问题是代码行

instance.remainingAmount = instance.remainingAmount - allowanceToCheck

在装饰器中,我试图设置类的变量不起作用。

有什么方法可以解决这个问题吗?

1 个答案:

答案 0 :(得分:4)

您已将Salary.basic置于某个媒体资源中,Salary.basic()会产生副作用!因此,每当您的其他功能引用self.basic时,它会重新计算self.RemainingAmount并将其重置为原始值self.grossPay - basic

具有这种副作用的属性是糟糕的设计。我希望你现在明白为什么。即使您解决了这个问题,以不同的顺序访问其他属性也会得到不同的结果。属性访问器不应具有持久的副作用。更一般地说: Setter必须设置,getter必须得到。属性看起来像这样的简单变量,因此它们必须相应地运行,否则你将永远无法再理解或调试代码