Python OOP咖啡机代码调用get_items报缺少self参数错误求助
OOP咖啡机程序self参数缺失报错排查
问题场景
- 学习Udemy平台Python课程时,编写基于面向对象(类与对象)实现的咖啡机程序
- 课程演示代码可正常运行,本地运行抛出如下报错:
Traceback (most recent call last): File "c:\Users\Armand S\Desktop\Python Files\D16 Coffee Machine in OOP.py", line 123, in <module> options = menu.get_items() TypeError: get_items() missing 1 required positional argument: 'self'
- 报错位置在while循环内调用
menu.get_items()处,不理解实例方法为何提示需要传入self位置参数,需要排查修复方案,同时收集代码优化建议
原始问题代码
#Coffee Machine in OOP code #Define the Moneymachine class first to process the money and currency received class MoneyMachine: CURRENCY = "$" #write the coin values in a dictionary COIN_VALUES = { "quarters" : 0.25, "dimes" : 0.1, "nickles" : 0.05, "pennies" : 0.01 } #we write functions using init function to initialize def __init__(self): self.profit = 0 self.money_received = 0 def report(self): #Prints the report of the current profit print(f"Money: {self.CURRENCY}{self.profit}") def process_coins(self): #Returns the value of the total calculated coins inserted print("Please insert coins.") for coin in self.COIN_VALUES: self.money_received += int(input(f"How many {coins}? ")) * self.COIN_VALUES[coin] return self.money_received #define the payment function def make_payment(self,cost): self.process_coins() if self.money_received >= cost: change = round(self.money_received - cost, 2) print(f"Here is your {self.CURRENCY}{change} in change.") self.profit += cost self.money_received = 0 return True #if payment is not enough, return False else: print("Sorry that's not enough money. Money refunded.") self.money_received = 0 return False #Next we define the Menu class and Menu items. We can model it using classes class menuItem: #put in all the ingredient variables and the cost ofcourse def __init__(self, name, water, milk, coffee, cost): self.name = name self.cost = cost self.ingredients = { "water" : water, "milk" : milk, "coffee": coffee } #full menu with costs for different coffees class menu: """Models the Menu with drinks.""" def __init__(self): self.menu = [ MenuItem(name="latte", water=200, milk=150, coffee=24, cost=2.5), MenuItem(name="espresso", water=50, milk=0, coffee=18, cost=1.5), MenuItem(name="cappuccino", water=250, milk=50, coffee=24, cost=3), ] def get_items(self): #Returns all the names of the available menu items options = "" for item in self.menu: options += f"{item.name}/" return options def find_drink(self, order_name): #Searches the menu for a particular drink by name. Returns that item if it exists, otherwise returns None for item in self.menu: if item.name == order_name: return items print("Sorry that item is not available") #After this we make the CoffeeMaker class to process the coffee class CoffeeMaker: #models the coffee Machine def __init__(self): self.resources = { "water" : 300, "milk" : 200, "coffee": 100, } def report(self): #prints out the report of the resources: print(f"Water: {self.resources['water']}ml") print(f"Milk: {self.resources['milk']}ml") print(f"Coffee: {self.resources['coffee']}g") def is_resource_sufficient(self,drink): #Returns True when order can be made, False if ingredients are insufficient can_make = True for item in drink.ingredients: if drink.ingredients[item] > self.resources[item]: print(f"Sorry there is not enough {item}.") can_make = False return can_make def make_coffee(self,order): #Deducts the required ingredients from the resources for item in order.ingredients: self.resouces[item] -= order.ingredients[item] print(f"Here's your {order.name}. Enjoy!") #----------------------------------------------------------------------Python Part-----------------------------------------------------------------------# money_machine = MoneyMachine() coffee_maker = CoffeeMaker() is_on = True coffee_maker.report() money_machine.report() while is_on: options = menu.get_items() choice = input(f"What would you like? ({options}): ") if choice == "off": is_on = False elif choice == "report": coffee_maker.report() money_machine.report() else: drink = menu.find_drink(choice) if coffee_maker.is_resource_sufficient(drink) and money_machine.make_payment(drink.cost): coffee_maker.make_coffee(drink)
报错根因
Python类中定义的普通实例方法,第一个参数self会在通过类实例调用方法时,由Python自动传入,值为实例对象本身。
核心问题是没有创建menu类的实例对象,直接用类名调用了实例方法get_items(),Python无法自动绑定self参数,就会抛出缺少位置参数的错误。
除此之外代码里还有4处拼写/大小写错误,修复完核心实例化问题后依然会导致运行崩溃:
- 类名大小写不一致:定义的类是小写开头的
menuItem,但menu类初始化时引用的是大写开头的MenuItem,会触发NameError process_coins方法内循环变量是coin,输入提示里写成了coins(多了s),变量未定义find_drink方法内匹配到饮品后返回items(多了s),实际循环变量是item,变量未定义make_coffee方法内操作资源时写的是self.resouces(少了字母r),实际属性名是self.resources,属性不存在
修复方案
- 按Python PEP8规范修正类名大小写:将
class menuItem改为class MenuItem,class menu改为class Menu,保持类名引用一致 - 主程序初始化阶段,和
MoneyMachine、CoffeeMaker一样,先创建Menu类的实例:
menu = Menu()
- 修正所有拼写错误:
- 投币输入提示的
{coins}改为{coin} - 查找饮品方法的返回值
items改为item - 制作咖啡方法的
self.resouces改为self.resources
- 投币输入提示的
修复后可运行完整代码
class MoneyMachine: CURRENCY = "$" COIN_VALUES = { "quarters" : 0.25, "dimes" : 0.1, "nickles" : 0.05, "pennies" : 0.01 } def __init__(self): self.profit = 0 self.money_received = 0 def report(self): print(f"Money: {self.CURRENCY}{self.profit}") def process_coins(self): print("Please insert coins.") for coin in self.COIN_VALUES: self.money_received += int(input(f"How many {coin}? ")) * self.COIN_VALUES[coin] return self.money_received def make_payment(self,cost): self.process_coins() if self.money_received >= cost: change = round(self.money_received - cost, 2) print(f"Here is your {self.CURRENCY}{change} in change.") self.profit += cost self.money_received = 0 return True else: print("Sorry that's not enough money. Money refunded.") self.money_received = 0 return False class MenuItem: def __init__(self, name, water, milk, coffee, cost): self.name = name self.cost = cost self.ingredients = { "water" : water, "milk" : milk, "coffee": coffee } class Menu: """Models the Menu with drinks.""" def __init__(self): self.menu = [ MenuItem(name="latte", water=200, milk=150, coffee=24, cost=2.5), MenuItem(name="espresso", water=50, milk=0, coffee=18, cost=1.5), MenuItem(name="cappuccino", water=250, milk=50, coffee=24, cost=3), ] def get_items(self): options = "" for item in self.menu: options += f"{item.name}/" return options def find_drink(self, order_name): for item in self.menu: if item.name == order_name: return item print("Sorry that item is not available") return None class CoffeeMaker: def __init__(self): self.resources = { "water" : 300, "milk" : 200, "coffee": 100, } def report(self): print(f"Water: {self.resources['water']}ml") print(f"Milk: {self.resources['milk']}ml") print(f"Coffee: {self.resources['coffee']}g") def is_resource_sufficient(self,drink): if not drink: return False can_make = True for item in drink.ingredients: if drink.ingredients[item] > self.resources[item]: print(f"Sorry there is not enough {item}.") can_make = False return can_make def make_coffee(self,order): for item in order.ingredients: self.resources[item] -= order.ingredients[item] print(f"Here's your {order.name}. Enjoy!") if __name__ == "__main__": money_machine = MoneyMachine() coffee_maker = CoffeeMaker() menu = Menu() is_on = True coffee_maker.report() money_machine.report() while is_on: options = menu.get_items() choice = input(f"What would you like? ({options}): ").lower() if choice == "off": is_on = False elif choice == "report": coffee_maker.report() money_machine.report() else: drink = menu.find_drink(choice) if coffee_maker.is_resource_sufficient(drink) and money_machine.make_payment(drink.cost): coffee_maker.make_coffee(drink)
可选优化建议
- 遵循PEP8命名规范:类名使用大驼峰格式,方法、变量名使用蛇形格式,缩进统一用4个空格,避免大小写、拼写错误
- 投币逻辑增加输入校验,捕获用户输入非整数的异常,避免程序直接崩溃
find_drink方法匹配不到饮品时显式返回None,同时资源检查逻辑增加None判断,避免访问空对象属性报错- 用户输入统一做小写转换,避免用户输入大写首字母的饮品名时匹配失败
- 资源不足时可以一次性列出所有缺少的原料,不用每次运行只提示一种原料不足
内容的提问来源于stack exchange,提问作者Armand Surbakti
相关产品推荐
相关产品推荐

