【问题标题】:Using __init__ in OOP在 OOP 中使用 __init__
【发布时间】:2020-07-25 23:10:14
【问题描述】:

我正在学习使用 OOP 开发代码。但是,我在理解何时使用 __init__ 构造函数时遇到问题。 __init__ 在 OOP 中是强制性的吗?如果是这样,我将如何使用__init__

以下代码的作用是获取用户请求的比萨饼大小和配料以及退货和最终总数。

当我运行以下代码时:

class Pizza:
    """ customer orders pizza size and pizza toppings"""

    def size_menu(self): # Provides user a menu
        
        self.menu_s = """
        What size pizza would you like?
            _____________________________________________________________
            | 1: Small  |  2: Large  |  3: Extra Large  |  4: Party Size |
            |  $6.23    |   $10.23   |      $12.23      |      $24.23    |
            |___________|____________|__________________|________________|
            """
        print(self.menu_s)
        return self.menu_s

    def size_order(self): # Gets size wanted and returns pizza total. 
        size_mappings = {
            1: "Small",
            2: "Large",
            3: "Extra Large",
            4: "Party Size"
            }

        cost_mappings = {
            "Small": 6.23,
            "Large": 10.23,
            "Extra Large": 12.23,
            "Party Size": 24.23
            }

        response = input('-') # user inters 1-4 for pizza size wanted and returns a size total.
        self.size_wanted = float(response) # Turns response as a float
        self.size_wanted = size_mappings[self.size_wanted] # Size requested
        self.size_cost = cost_mappings[self.size_wanted] # Cost of size

        print(f"Getting your {self.size_wanted} pizza ready.")
        print(f"Your current total is: ${self.size_cost}")
        return self.size_cost

    def topping_menu(self): # Provides user with toppings menu
        self.menu_t = """
        What toppings do you want on your pizza?
        _____________________________________________________
       |   1:Bacon      |  4:Anchovies     |  7:Black Olives |
       |   2:Pepperoni  |  5:Spinach       |  8:Chicken      |
       |   3:Mushrooms  |  6:Onions        |  9:Ground Beef  |
       |________________|__________________|_________________| 
       What toppings do you want on your pizza?
       """
        print(self.menu_t)
        return self.menu_t
       

    def topping_order(self): # Gets toppings the user wants and returns a total of all toppings. 
        topping_mappings = {
            1: 'Bacon', 
            2: 'Pepperoni', 
            3: 'Mushrooms', 
            4: 'Anchovies', 
            5: 'Spinach', 
            6: 'Onions', 
            7: 'Black Olives',
            8: 'Chicken', 
            9: 'Ground Beef'
            }

        self.requested_toppings = []

        while True:
            response = input('-')

            if response == 'q':
                break

            toppings_wanted = response
            toppings_wanted = topping_mappings[int(toppings_wanted)]
            self.requested_toppings.append(toppings_wanted)

            if toppings_wanted in topping_mappings.values():
                print(f"Adding: {toppings_wanted}")

            else:
                print(f"We do not have {toppings_wanted}")

        self.topping_total = len(self.requested_toppings) * float(1.23)

        print("\nWe are adding the requested toppings to your pizza.")
        print(f"your topping total will be: ${self.topping_total}")
        return self.topping_total

   
    def final_total(self):
        total = self.size_cost + self.topping_total
        total = float(total)
        print(f"\nYour final order total will be ${total}")



if __name__ == '__main__':
    
    customer_order = Pizza()
    customer_order.size_menu()
    customer_order.size_order()
    customer_order.topping_menu()
    customer_order.topping_order()
    customer_order.final_total()
    
    

我想知道如果程序返回我正在寻找的信息,我为什么要使用__init__ 构造函数? 感谢您的帮助。

【问题讨论】:

  • 把这些函数写成一个类有什么好处?您可以在一个模块中编写这些函数,导入该模块并实现类似的效果(如果不是相同的话)——有人会说这将是在 Python 中执行此操作的正确方法。
  • @wwii 感谢您的资源。我已经阅读了 Python 文档,但是仍然很难理解。我知道我可以将它们写入一个模块,但是我认为这段代码是解决问题和专注于 OOP 的绝佳机会。

标签: python class oop


【解决方案1】:

虽然此代码有效,但它的可扩展性和可重用性都不是很好。

如果明天您希望允许使用来自 json 文件的输入而不是用户输入来订购披萨,该怎么办?

如果您忘记调用order 方法之一怎么办?对final_total 的调用将使您的程序崩溃,因为某些属性会丢失。

此外,在 __init__ 方法之外创建属性被认为是一种反模式,因为它使代码难以阅读、难以理解和难以使用(目前,并非所有 Pizza 实例都具有始终相同的属性)。

如何让它变得更好

  1. 将所有硬编码的永久值移动为类属性。这些将在Pizza 的所有实例之间共享。

  2. 获取Pizza 需要的所有参数,以便成为__init__ 中的Pizza。这些对于每个Pizza 都是唯一的。

  3. 实现订购Pizza 的可能方法。其中之一可能是from_user_input

请注意,此代码使用的 Python 概念可能比您目前可能知道的更高级一些。以此为契机学习。 它远非完美(例如,它缺少一些非常基本的错误检查和处理),但它是一个很好的起点。

class Pizza:
    size_mappings = {
        1: "Small",
        2: "Large",
        3: "Extra Large",
        4: "Party Size"
    }
    cost_mappings = {
        "Small": 6.23,
        "Large": 10.23,
        "Extra Large": 12.23,
        "Party Size": 24.23
    }
    cost_per_topping = 1.23
    topping_mappings = {
        1: 'Bacon',
        2: 'Pepperoni',
        3: 'Mushrooms',
        4: 'Anchovies',
        5: 'Spinach',
        6: 'Onions',
        7: 'Black Olives',
        8: 'Chicken',
        9: 'Ground Beef'
    }
    size_menu = """
        What size pizza would you like?
            
            1: Small         ${Small}
            2: Large         ${Large}
            3: Extra Large   ${Extra Large} 
            4: Party Size    ${Party Size}\n\n"""

    def __init__(self, size_wanted, requested_toppings):
        self.size_wanted = size_wanted
        self.requested_toppings = requested_toppings

    def finalize_order(self):
        cost = self.cost_mappings[self.size_mappings[self.size_wanted]] + len(self.requested_toppings) * self.cost_per_topping
        print("Thanks for ordering. The final cost is {}".format(cost))

    @classmethod
    def show_size_menu(cls):
        return cls.size_menu.format(**cls.cost_mappings)

    @classmethod
    def show_toppings_menu(cls):
        return "What toppings would you want on your Pizza?\n{}".format(
            '\n'.join('{}: {}'.format(k, v) for k, v in cls.topping_mappings.items())
        )

    @classmethod
    def from_user_input(cls):
        size_wanted = int(input(cls.show_size_menu()))
        requested_toppings = []
        print(cls.show_toppings_menu())
        print("Type requested toppings' numbers, and 'q' when done")
        while True:
            req_topping = input()
            if req_topping == 'q':
                break
            try:
                requested_toppings.append(int(req_topping))
            except ValueError:
                print('Only numbers or q')
        return cls(size_wanted, requested_toppings)


p = Pizza.from_user_input()
p.finalize_order()

好处:

  1. 所有常量值都在一个位置,就在class Pizza 下方。如果我们需要更改某些内容,我们确切地知道它在哪里。

  2. Pizza 类与创建方法解耦,并不依赖于我们每次创建实例时都以正确的顺序调用 5 个方法。

  3. 如果明天有人要求我们从 json 文件中创建一个Pizza,那么只需实现def from_json_file

上述代码的示例执行:

What size pizza would you like?
            
            1: Small         $6.23
            2: Large         $10.23
            3: Extra Large   $12.23 
            4: Party Size    $24.23

2
What toppings would you want on your Pizza?
1: Bacon
2: Pepperoni
3: Mushrooms
4: Anchovies
5: Spinach
6: Onions
7: Black Olives
8: Chicken
9: Ground Beef
Type requested toppings' numbers, and 'q' when done
1
3
7
q
Thanks for ordering. The final cost is 13.92

【讨论】:

  • 哇,非常感谢您花时间重新格式化所有内容并提供您的见解。您的解释有助于我更多地理解 init,我只需要花一些时间来处理它。还要感谢您添加一些不熟悉的概念,这让我有一些工作要做。再次感谢您!
  • 当我开始查看您提供的示例时,我注意到一些问题。您使用 .format() 而不是“f 字符串”是否有原因,或者只是个人喜好?您还使用'cls'只是为了区分不同的功能吗?
  • @ChrisJaurigue 1. 我倾向于在 stackoverflow 上使用 .format,因为 f 字符串仅在 Python 3.6 中引入,因此无论他们使用的 Python 版本如何,使用 .format 对每个人都有效。 2. 我在类方法中使用cls 来引用类本身。类方法的第一个参数(标记为@classmethod)是类本身(在本例中为Pizza),就像“普通”(实例)方法的第一个参数是实例本身(self)一样。使用名称cls 是一种约定,就像使用名称self 是一种约定一样
【解决方案2】:

在此代码中,您实际上并不需要 __init__。当您有一个希望从中创建具有不同参数的多个实例的类时,您需要它。比如说,你有一个班级 PizzaRestaurant 而不是 Pizza,但是不同的餐厅可以有不同的披萨选择和不同的价格,那么你可以这样做:

class PizzaRestaurant:
    def __init__(self, menu: dict) -> None:
        self.menu = menu

因此,现在您可以初始化PizzaRestaurant 的多个实例,其中每个餐厅可以以字典的形式向构造函数提供不同的菜单,其中包含 3 种不同尺寸的比萨饼类型和价格:

johns_menu = {'hawaiian': [10, 12, 15], 'capriciosa' : [11, 13, 16], 'margherita' : [9, 10, 12]}

johns_pizzeria = PizzaRestaurant(johns_menu)

这里的字典 johns_menu 将被传递给 __init__ 构造函数,该构造函数将初始化类属性,然后您可以使用这组特定的比萨饼类型和价格计算订单总数。

但是您也可以创建另一个实例,例如 Luigi's pizzeria,它将有不同的选择和不同的价格,在这种情况下,您只需在调用类时提供不同的字典。

如果一个类只是做一些计算并返回结果:a)你不需要__init__b) 它不需要是一个类——它可以像一个函数一样工作。

【讨论】:

    猜你喜欢
    • 2019-03-08
    • 1970-01-01
    • 1970-01-01
    • 2019-01-17
    • 1970-01-01
    • 2020-03-21
    • 2013-12-25
    • 2022-12-11
    • 2017-07-01
    相关资源
    最近更新 更多