【问题标题】:Shorten if-elif-elif...-else缩短 if-elif-elif...-else
【发布时间】:2021-12-31 08:04:04
【问题描述】:

我有一个程序,其中一个功能是出售用户拥有的物品。它提示用户输入名称(id)和金额,然后出售。但是用户可以拥有很多项目,所以有很多 if else elif 语句。我该如何缩短这个? (P.S.我用的是Replit,Replit目前有Python 3.8)这里是sell函数,供参考。

def sell_command():
  global cash
  cash = 0

  #I created a dictionary, inventory, which has how much the user has of a particular item.

  #itemSell variable contains what the user wants to sell
  #itemSellCount variable contains how much the user wants to sell
  #itemSoldCash variable calculates how much one item is worth, and multiplies for how much the user is selling
  #cash variable is hlobal since another function prints cash
  
  itemSell = input('What would you like to sell? ')
  itemSell = itemSell.lower()

  
  if itemSell == "cobblestone" or "cobble stone":
    itemSellCount = int(input("How many would you like to sell? "))
    if itemSellCount <= inventory["cobblestone"]:
      itemSoldCash = itemSellCount*10
      print("You sold " + str(itemSellCount) + " cobblestone/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["cobblestone"] -= itemSellCount
    elif itemSellCount > inventory["cobblestone"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "coal":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["coal"]:
      itemSoldCash = itemSellCount*5
      print("You sold " + str(itemSellCount) + " coal for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["coal"] -= itemSellCount
    elif itemSellCount > inventory["coal"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "iron ore" or "ironore":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["ironOre"]:
      itemSoldCash = itemSellCount*20
      print("You sold " + str(itemSellCount) + " iron ore/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["cobblestone"] -= itemSellCount
    elif itemSellCount > inventory["ironOre"]:
      print("You tried to sell more than what you have!")
    elif itemSell == "iron ingot" or "ironingot":
      itemSellCount = int(input("How many would you like to sell?"))
      if itemSellCount <= inventory["ironIngot"]:
        itemSoldCash = itemSellCount*25
        print("You sold " + str(itemSellCount) + " iron ingot/s for $" + str(itemSoldCash))
        cash = cash + itemSoldCash
        inventory["ironIngot"] -= itemSellCount
      elif itemSellCount > inventory["ironIngot"]:
        print("You tried to sell more than what you have!")
  elif itemSell == "emerald" or "emeralds":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["emerald"]:
      itemSoldCash = itemSellCount*100
      print("You sold " + str(itemSellCount) + "emerald/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["emerald"] -= itemSellCount
    elif itemSellCount > inventory["emerald"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "diamond" or "diamonds":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["diamond"]:
      itemSoldCash = itemSellCount*300
      print("You sold " + str(itemSellCount) + " diamond/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["diamond"] -= itemSellCount
    elif itemSellCount > inventory["diamond"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "oak":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["oak"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " oak/s for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["oak"] -= itemSellCount
    elif itemSellCount > inventory["oak"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "birch":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["birch"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " birch for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["birch"] -= itemSellCount
    elif itemSellCount > inventory["birch"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "redwood" or "red wood":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["redwood"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + "redwood for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["redwood"] -= itemSellCount
    elif itemSellCount > inventory["redwood"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "spruce":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["spruce"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " spruce for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["spruce"] -= itemSellCount
    elif itemSellCount > inventory["spruce"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "acacia":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["acacia"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " acacia for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["acacia"] -= itemSellCount
    elif itemSellCount > inventory["acacia"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "jungle":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["jungle"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " jungle for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["jungle"] -= itemSellCount
    elif itemSellCount > inventory["jungle"]:
      print("You tried to sell more than what you have!")
  elif itemSell == "maple":
    itemSellCount = int(input("How many would you like to sell?"))
    if itemSellCount <= inventory["maple"]:
      itemSoldCash = itemSellCount*15
      print("You sold " + str(itemSellCount) + " maple for $" + str(itemSoldCash))
      cash = cash + itemSoldCash
      inventory["maple"] -= itemSellCount
    elif itemSellCount > inventory["maple"]:
      print("You tried to sell more than what you have!")

【问题讨论】:

  • 首先,itemSell == "cobblestone" or "cobble stone" 始终为真,因此实际上没有运行以下 else 语句(因此,如果您想缩短代码并具有相同的行为,则可以将它们全部删除)。 .. 例如,您是否尝试过输入煤炭?除此之外,请将帖子缩减为minimal reproducible example
  • 第一个如果有错别字,在第二个条件下它总是正确的
  • 使用字典和辅助函数来避免几乎相同的块

标签: python if-statement python-3.8


【解决方案1】:

对于本质上相同的东西,有很多重复的代码(我已经稍微清理了你的语法):

itemSellCount = int(input("How many would you like to sell? "))
if itemSellCount <= inventory[itemSell]:
  itemSoldCash = itemSellCount*10
  print(f"You sold {itemSellCount} {itemSell}/s for ${itemSoldCash}")
  cash += itemSoldCash
  inventory[itemSell] -= itemSellCount
else:
  print("You tried to sell more than what you have!")

但是,需要考虑三件事:

1.每件商品的售价是多少?

这可以通过多种方式解决,具体取决于编程风格以及您需要跟踪的每个项目的内容。 OOP 方法是创建一个项目类,每个项目都有一些属性来定义它的值。一个简单的程序方法是有一个定义这个的字典:

itemValue = {
    "cobblestone": 10,
    "coal": 5,
    ...
}

现在,使用字典查找来确定itemSoldCash

itemSoldCash = itemSellCount*itemValue[itemSell]

2。替代项目名称

您接受替代项目名称,例如“鹅卵石”被视为“鹅卵石”。这也可以通过字典来解决,例如类似:

itemAltNames = {
    "cobble stone": "cobblestone",
    "iron ingot": "iron ingot",
    ...
}

然后,您可以执行以下操作:

if itemSell in itemAltNames:
    itemSell = itemAltNames[itemSell];

或者,如果您的替代方案只涉及剥离空格,那么就这样做:

itemSell = itemSell.replace(" ","")

3.检查项目是否存在

就目前而言,如果用户输入了无效项,您的控制流将不会执行。这很好,但过于复杂!此外,如果用户输入无效项目,您是否会给出错误消息(或允许重复输入)?检查您的库存字典以确保用户拥有该项目:

if itemSell in inventory:

把它们放在一起

这就是现在一切的样子:

def sell_command():

    global cash
    cash = 0

    itemSell = input("What would you like to sell? ")
    while (itemSell := itemSell.lower().strip().replace(" ","")) not in inventory:
        itemSell = input(f"You do not have {itemSell} in your inventory. What would you like to sell? ")

    itemSellCount = int(input("How many would you like to sell? "))
    if itemSellCount <= inventory[itemSell]:
        itemSoldCash = itemSellCount * itemValue[itemSell]
        print(f"You sold {itemSellCount} {itemSell}/s for ${itemSoldCash}")
        cash += itemSoldCash
        inventory[itemSell] -= itemSellCount
    else:
        print("You tried to sell more than what you have!")

【讨论】:

  • 谢谢!但是 3. 检查项目是否存在,在我的判断中,键是项目的名称,值是用户拥有的项目的数量。 'if itemSell in inventory:' 会起作用吗?
  • 当然;如果键存在于字典中,if 条件将返回 true。或者,我提供的 while 循环会继续提示该键是否在字典中不存在,然后在用户输入字典中确实存在的键时继续。
  • 我稍微更改了代码,以便将输入中的木头替换为空。此外,当我试图卖出比我拥有的更多的东西时,它会恢复我的平衡。这是为什么呢?
  • 你的意思是,它会重置你的余额?如果您尝试销售的数量超过您拥有的数量,则编写的代码不应调整库存余额。或者你的意思是现金余额?您的sell_command 在函数内部设置cash = 0; cash 可能应该在函数之外初始化为 0(即全局),那么在函数中唯一应该修改 cash 的地方是 cash += itemSoldCash
  • 好的,再次感谢您的帮助!
【解决方案2】:

您可以使用字典和对象来大量减少分支。这就是 OOP 的力量。这是一个如何重构代码的示例。

cash = 0


class CommodityShelf:
    """
    A shelf to hold commodities
    """
    def __init__(self, item_name, available_quantity, price):
        self.item_name = item_name
        self.available_qty = available_quantity
        self.price = price or 10

    def key(self):
        # "make it easy to search for this shelf"
        return self.item_name.lower().replace(" ", "")

    def sell(self):
        # handle the selling logic at one place, for any item
        global cash
        sell_units = int(input(f"How many {self.item_name} would you like to sell? "))
        if sell_units > self.available_qty:
            print(f"You tried to sell more {self.item_name} than what you have!")
        else:
            cash += (sell_units * self.price)
            self.available_qty -= sell_units
            print(f"You sold {self.item_name} for ${sell_units * self.price}")


def start_selling(pos_registry):
    """
    Start selling commodities
    :param pos_registry:
    :return:
    """
    item_to_sell = input('What would you like to sell? ').lower().replace(" ", "")
    if item_to_sell in pos_registry:
        pos_registry[item_to_sell].sell()
    else:
        print(f"You tried to sell {item_to_sell} but it's not in your inventory!")


if __name__ == '__main__':
    # create a shelf for each commodity
    inventory = [
        CommodityShelf("Cobble Stone", 10, 0),
        CommodityShelf("Coal", 10, 0),
        CommodityShelf("Iron Ore", 10, 0),
        CommodityShelf("Iron Ingot", 10, 0),
        CommodityShelf("Emarald", 10, 0),
        CommodityShelf("Diamond", 10, 0),
    ]
    # create a registry of all the shelves
    pos_registry = {pos.key(): pos for pos in inventory}

    # start selling
    start_selling(pos_registry)
    
    # loop it if the user wants to
    

我希望逻辑解释在 cmets 中。

【讨论】:

    【解决方案3】:

    看来你的很多 if-else 是多余的。在每个 if 条件中唯一唯一的是 itemSoldCash。您可以使用辅助函数将它们映射到相关的itemSells,否则使用单个 if-else 条件:

    def sell_command():
        global cash
        cash = 0
        
        def get_Cash(itemSell, itemSellCount):
            if itemSell == "cobblestone":
                return itemSellCount*10
            elif itemSell == "coal":
                return itemSellCount*5
            elif itemSell == "ironore":
                return itemSellCount*20
            elif itemSell == "ironingot":
                return itemSellCount*25
            elif itemSell == "emerald":
                return itemSellCount*100
            elif itemSell == "diamond":
                return itemSellCount*300
            elif itemSell in ["oak","birch","redwood","spruce","acacia","jungle","maple"]:
                return itemSellCount*15
      
        itemSell = ''.join(input('What would you like to sell? ').lower())
        itemSellCount = int(input("How many would you like to sell? "))
        
        if itemSellCount <= inventory[itemSell]:
            itemSoldCash = get_Cash(itemSell, itemSellCount)
            print("You sold {} {} for ${}".format(itemSellCount, itemSell, itemSoldCash))
            cash += itemSoldCash
            inventory[itemSell] -= itemSellCount
        else:
            print("You tried to sell more than what you have!")
    
        
    

    【讨论】:

      【解决方案4】:

      此代码可能是最简单的修复方法。不过,您可能必须更改清单字典,以便每个键都是小写的。

      另外,itemSell == "cobblestone" or "cobble stone" 总是返回 true。

      def sell_command():
        global cash
        cash = 0
        cashMultiplierDict = {"cobblestone": 10, "coal": 5} #etc
        #The following line just removes all white spaces (e.g. cobble stone becomes cobblestone)
        itemSell = input('What would you like to sell? ').lower().replace(" ", "")
        itemSellCount = int(input("How many would you like to sell? "))
        if itemSellCount <= inventory[itemSell]:
          itemSoldCash = itemSellCount * cashMultiplierDict[itemSell] #Uses the dict created before
          print("You sold " + str(itemSellCount) + " " + itemSell + "/s for $" + str(itemSoldCash))
          cash = cash + itemSoldCash
          inventory[itemSell] -= itemSellCount
        elif itemSellCount > inventory[itemSell]:
          print("You tried to sell more than what you have!")
      

      【讨论】:

        【解决方案5】:

        您可以更好地组织代码:

        itemSellCount = int(input("How many would you like to sell?")
        

        对每个项目都进行,可以这样做一次,然后

        itemSoldCash = itemSellCount*15
        

        是成本的数量。成本可以以更好的数据结构组织到一个目录中,可能是这样的:

        # you can add if needed other attributes for all material, like color, weight, damage, duration, and so on
        catalogue = {
            "cobblestone": {"cost": 15, 'color':'red'},
            "maple": {"cost": 15},
            "jungle": {"cost": 15},
            "acacia": {"cost": 15},
            "diamond": {"cost": 300},
            ...
        }
        
        # aliases
        catalogue["cobble stone"] = catalogue["cobblestone"]
        
        
        def sell(item, qty, inventory):
            if qty <= inventory[item]:
                sold = qty * catalogue[item]["cost"]
                print("You sold {} maple for ${}".format(qty, sold))
                cash += sold
                inventory[item] -= qty
            else:
                print("You tried to sell more {} than what you have!".format(item))
        

        最后你用所有需要的信息调用这个函数:

        itemSell = input('What would you like to sell? ')
        itemSell = itemSell.lower()
        itemSellCount = int(input("How many would you like to sell? "))
        sell(itemSell,itemSellCount, inventory)
        

        示例中未定义库存。

        PS 如果许多对象的成本相同,并且它们被视为默认成本,您可以 简化目录定义,只放入特殊成本,默认可以作为缺失值应用:

        sold = qty * (catalogue[item]["cost"] if item in catalogue else 15)
        

        【讨论】: