使用实例方法作为装饰器修饰类内方法时触发TypeError的问题
类内实例方法作为装饰器使用@语法糖报错的问题排查与解决
问题场景
使用Car类的实例方法decor作为装饰器修饰类内的car_details方法时,手动调用装饰逻辑(先创建实例再绑定方法)代码可正常运行,但使用@decor语法糖时触发错误:
TypeError: decor() missing 1 required positional argument: 'func'
手动可行的调用代码片段:
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)
报错的完整代码:
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)
错误原因
类在加载定义阶段,还没有创建任何实例。@decor语法糖直接调用时,decor作为实例方法需要第一个参数是self,但此时没有实例可以传入,Python会把被装饰的car_details方法当成self参数传递给decor,导致decor缺少了原本必需的func参数,从而抛出参数缺失的错误。
而手动调用时,是先创建Car实例obj,再通过obj.decor获取绑定了实例的方法(self已自动传入),此时调用decor(details)只需要传入func参数,因此可以正常运行。
解决方法
最优方案:实例化后手动绑定装饰器
不在类定义阶段使用@语法糖,而是在__init__方法中,实例创建完成后手动将car_details传入decor,得到装饰后的方法再赋值回属性。这样既保留装饰逻辑,又避开类加载阶段无实例的问题。
修改后的完整代码:
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 # 实例化后手动应用装饰器 self.car_details = self.decor(self.car_details) 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 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) # 直接调用装饰后的方法,传入tax参数 obj.car_details(ans)
方案说明
- 移除
car_details上的@decor语法糖,恢复为普通实例方法 - 在
__init__中,实例初始化完成后,调用self.decor(self.car_details)得到装饰后的方法,重新赋值给self.car_details - 主函数中直接调用
obj.car_details(ans)即可,无需手动处理wrapper
这种方式逻辑直观,完全适配原有的装饰器逻辑,同时避免了类定义阶段的实例绑定问题。
内容的提问来源于stack exchange,提问作者Navalkishor Savita
相关产品推荐
相关产品推荐

