decorating method using instance method

302 Views Asked by At

I am trying to decorate my instance car_details with another instance method decor but am not able to figure out why this is giving the error. while i call the call car_details method like below

if __name__ == '__main__':
    car_type = input("Enter type of car: ")
    price = int(input("Enter price of car: "))
    obj = Car(car_type, price)
    decor = obj.decor
    details = obj.car_details
    tax = {1: "Yes", 2: "No"}
    to_apply = int(input("Select 1 for applying tax and 0 for No:"))
    ans = tax.get(to_apply)
    res = decor(details)
    res(ans)

then it works properly, but while i try it with the @decorator at that time i get issue.

TypeError: decor() missing 1 required positional argument: 'func'

TYPE = {'hybrid': 'Hybrid', 'petrol': 'Petrol', 'deasel': 'Deasel'}

def tax_price(car_type):
    if car_type == 'hybrid':
        SGST = 300000
        GST = 500000
    elif car_type == 'petrol':
        SGST = 200000
        GST = 400000
    elif car_type == 'deasel':
        SGST = 100000
        GST = 300000
    return SGST+GST

class Car:
    def __init__(self, car_type, price):
        self.car_type = car_type
        self.price = price

    def decor(self, func):
        def wrapper(apply_tax):
            if apply_tax == 'Yes':
                car_tax = tax_price(self.car_type)
                self.price += car_tax
                func()
            else:
                func()
        return wrapper
    @decor
    def car_details(self):
        print("Car Type:", self.car_type)
        print("Car Price: ", self.price)


if __name__ == '__main__':
    car_type = input("Enter type of car: ")
    price = int(input("Enter price of car: "))
    obj = Car(car_type, price)
    tax = {1: "Yes", 2: "No"}
    to_apply = int(input("Select 1 for applying tax and 0 for No:"))
    ans = tax.get(to_apply)
    wrp = obj.car_details()
    wrp(ans)
1

There are 1 best solutions below

1
BareNakedCoder On

I see a few problems here. The biggest problem is you seem to be trying to use a decorator when a decorator is really (and I mean really) not suitable for this situation. If this code is not experimental and is to be maintained by someone, I strong suggest simplifying to:

def car_details(self, apply_tax):
    if apply_tax == 'Yes':
        car_tax = tax_price(self.car_type)
        self.price += car_tax
    print("Car Type:", self.car_type)
    print("Car Price: ", self.price)

But if you are just experimenting and want to make it work with decorators, I see the following problems:

  • When a decorator is called, the only arg passed to it is the function being decorated, not self which is not passed in at all. That means def decor(self, func): will need to change to def decor(func):.

  • When the decorated function, which is a method of a class, is invoked, the first arg will be self. That means def wrapper(apply_tax): will need to change to def wrapper(self, apply_tax):.

  • When the wrapped functions is invoked, it needs self as its only arg. That means func() will need to change to func(self) (in both places).

So, that gives:

def decor(func):
    def wrapper(self, apply_tax):
        if apply_tax == 'Yes':
            car_tax = tax_price(self.car_type)
            self.price += car_tax
            func(self)
        else:
            func(self)
    return wrapper

But, how does the decorated function get a value for apply_tax? It needs to be passed as an arg. So, you'll need ...

wrp = obj.car_details(ans)

Since this is a call on an instance, Python will automatically add self as the first arg and since car_details is decorated it will effectively be calling wrapper(self, ans).