【问题标题】:While Loop Ending Early in Python?虽然循环在 Python 中提前结束?
【发布时间】:2014-03-10 20:50:33
【问题描述】:

由于某种原因,我的 while 循环在两次尝试后停止,我无法弄清楚出了什么问题...... 它应该是一个蚂蚁农场,你可以在那里选择繁殖和制造新的蚂蚁等。 我只是不明白为什么它会停止... 这是我的代码:

import random

class Colony(object):
    workerAnts = 0
    list = []
    temp = []
    foodAmount = 10
    def breedWorker(self):
        if Colony.foodAmount < 5:
             print "Sorry! You do not have enough food to create a new worker ant!"
        else:
                Colony.foodAmount -= 5
                Colony.workerAnts += 1
                Colony.list.append("ant")
    def step(self):
        number = 'ant'
        for number in Colony.list:
            a = Ant()
            a.forage()
            if Colony.foodAmount > 0:
                Colony.foodAmount -= 1
            if Colony.foodAmount < len(Colony.list):
                for number in Colony.list[Colony.foodAmount+1:]:
                    Ant.health -= 1
    def purge(self):
        number = 'ant'
        for number in Colony.list:
            if Ant.health > 0:
                Colony.temp.append("ant")
        Colony.list = Colony.temp       

class Ant(object):  
    health = 10
    def forage(self):
        if Ant.health == 0:
            Colony.workerAnts -= 1
        if random.randint(0,100) > 95:
            Ant.health = 0
            print "Ant has died from a horrible accident!"
            Colony.workerAnts -= 1
        elif random.randint(0,100) < 40:
            newFood = random.randint(1,5)
            print "Ant has found %s food!!" % newFood
            Colony.foodAmount += newFood    
        elif random.randint(0,100) < 5:
            Ant.health = 10
            Colony.foodAmount += 10
            print "You've found sweet nectar! Your ant has returned to full health and has brought 10 food back to the colony!"
        else:
            print "Ant returned empty-handed!"
def main():
    queen = Colony()
    queen2 = Ant()
    while queen.workerAnts > 0 or queen.foodAmount >= 5:
        print "========================================================"
        print """
        Your colony has %s ants and %s food, Your Majesty.\nWhat would you like to do?\n0: Do nothing.\n1: Breed worker. (Costs 5 food.)""" % (queen.workerAnts, queen.foodAmount)
        answer = int(raw_input(">"))

        if answer != 1 and answer != 0:
            print "Sorry, invalid input!"
        if answer == 0:
            queen.step()
            queen.purge()
        if answer == 1:
            print "Breeding Worker..." 
            queen.breedWorker()
            queen.step()
            queen.purge()

    if queen.workerAnts <= 0 and queen.foodAmount < 5:
        print "I'm sorry! Your colony has died out!"

【问题讨论】:

  • 对于初学者,你不要在任何地方调用 main()
  • 而不是def main():if __name__ == '__main__':
  • 你的代码很乱,很难理解。我敢打赌这个问题与您使用类名(如变量)这一事实有关。在你的Colony 类中,你不应该做Colony.foodAmount 之类的东西,只需使用foodAmount
  • 或许你应该和this question's OP合作
  • 一个非常重要的一点是你不应该做例如elif random.randint(0,100) - 每次都会得到一个新的随机数!此外,&lt; 40&lt; 5 都将是 True,这不是为事件分配概率的正确方法。

标签: python loops while-loop


【解决方案1】:
  1. 您没有构造函数 (__init__(self, ...)),也没有初始化对象的属性
  2. 在方法中,您通过 self.property 而不是 Classname.property 调用此对象属性;在 python 中,您将实例或类对象显式传递给方法,按照惯例,它们应该是 'self' 例如,或 'cls' 代表类。
  3. 如果您想在 Ant 对象中使用任何 Colony 属性,反之亦然,您需要显式传递引用,并将其存储为属性。最明智的做法是通过调用 ants.append(Ant(self)) 来从 Colony 创建 Ant; Ant 的构造函数应该有签名 `def init(self, Colony):'

【讨论】:

    【解决方案2】:

    嗯,这是因为def purge(self) 中的以下行:

        Colony.list = Colony.temp
    

    第一次运行purge() 时,它使Colony.listColony.temp 都指向内存中的同一个数组。所以第二次运行purge(),你进入一个无限循环,在那里你for number in Colony.list: 执行Colony.temp.append("ant"),这实际上也增加了Colony.list,并且循环永远不会退出,因为它总是有一个新成员.

    在 python 中,for 循环为给定对象创建迭代器(如果它还不是迭代器)。在每次迭代中,python 都会调用迭代器的next() 方法(在本例中为列表)。如果next() 不能产生一个新值来迭代,它会引发StopIteration,并且循环退出。不用担心,for 语句会自动为您处理此异常。在您的情况下,Colony.list.next() 总是会找到一个新值(因为您刚刚附加到它),并且永远不会到达末尾。

    要修复您的代码,请尝试切片。这意味着复制数组,而不是将两个名称指向同一个数组:

        Colony.list = Colony.temp[:]
    

    【讨论】:

      【解决方案3】:

      您将Ant.health 设为类变量(在所有 Ant 实例之间共享)。

      只要一只蚂蚁的生命值降到 0,它们就会全部死亡。

      这是一个改进的版本。以下代码兼容 Python 2 和 3,我认为修复了所有错误!

      import random
      import sys
      
      if sys.hexversion < 0x3000000:
          # Python 2.x
          inp = raw_input
          rng = xrange
      else:
          # Python 3.x
          inp = input
          rng = range
      
      def get_int(prompt, lo=None, hi=None):
          """
          Prompt until an integer value in [lo..hi] is entered, then return it
          """
          while True:
              try:
                  val = int(inp(prompt))
                  if (lo is None or lo <= val) and (hi is None or val <= hi):
                      return val
              except ValueError:
                  pass
      
      class InsufficientFoodError(Exception):
          pass
      
      class Colony:
          def __init__(self, workers=0, food=10):
              self.food = food + Ant.cost * workers
              self.ants = []
              for i in rng(workers):
                  self.add_ant()
      
          def add_ant(self):
              try:
                  self.ants.append(Ant(self))
              except InsufficientFoodError as e:
                  print(e)
      
          def step(self):
              # all ants eat, then all ants forage:
              for ant in self.ants:
                  ant.eat()
              for ant in self.ants:
                  ant.forage()
              # bring out yer dead!
              self.ants = [ant for ant in self.ants if ant.is_alive()]
      
          def add_food(self, amount):
              self.food += amount
      
          def take_food(self, amount):
              amt = min(amount, self.food)
              self.food -= amt
              return amt
      
          def num_ants(self):
              return len(self.ants)
      
      class Ant:  
          cost = 5
          max_health = 10
      
          def __init__(self, colony):
              # try to get enough food to produce an ant
              food = colony.take_food(Ant.cost)
              if food < Ant.cost:
                  # Failed! return any taken food and throw an error
                  colony.add_food(food)
                  raise InsufficientFoodError('The colony does not have enough food to make a new Ant')
              else:
                  # Success!
                  self.colony = colony
                  self.health = Ant.max_health
      
          def eat(self):
              if self.health > 0:
                  self.health -= 1 - self.colony.take_food(1)
                  if self.health == 0:
                      print("An ant starved to death.")
      
          def forage(self):
              if self.is_alive():
                  dice = random.randint(0, 100)
                  if dice <= 5:
                      self.health = Ant.max_health
                      self.colony.add_food(10)
                      print("You've found sweet nectar! Your ant has returned to full health and has brought 10 food back to the colony!")
                  elif dice <= 40:
                      found_food = random.randint(1, 5)
                      self.colony.add_food(found_food)
                      print("Ant has found {} food!".format(found_food))
                  elif dice <= 95:
                      print("Ant returned empty-handed!")
                  else:
                      self.health = 0
                      print("Ant has died from a horrible accident!")
      
          def is_alive(self):
              return self.health > 0
      
      def main():
          colony = Colony()
      
          while True:
              print(
                 "========================================================\n"
                 "\n"
                 "Your colony has {ants} ants and {food} food, Your Majesty.\n"
                 "What would you like to do?\n"
                 "  1: Do nothing\n"
                 "  2: Breed worker (costs {cost} food)"
                 .format(ants=colony.num_ants(), cost=Ant.cost, food=colony.food)
              )
              opt = get_int("> ", 1, 2)
      
              if opt == 2:
                  print("Breeding Worker...")
                  colony.add_ant()
      
              colony.step()
      
              if colony.num_ants() == 0 and colony.food < Ant.cost:
                  print("I'm sorry! Your colony has died out!")
                  break
      
      if __name__=="__main__":
          main()
      

      【讨论】:

      • 确实,他们已经做了很多应该是实例属性的类属性
      【解决方案4】:

      这个答案有点不对劲,但似乎它将是一个有价值的知识。 这里的一个大问题是你的类被以一种不受欢迎的方式使用。

      类的主要优点是保存变量/函数的实例,这样您就可以对它们进行许多独立的分组。

      通过调用Colony.&lt;var&gt;,您正在更改基(或超)类变量的var。如果您只想拥有一个殖民地,这很有效,...但如果您想要两个,该怎么办。或三个!还是一百万!!?

      请注意,当您没有将self 作为类函数的第一个参数输入时,您是如何得到错误的?您需要意识到的是,您将类的实例作为第一个参数传递。这就是类知道要使用哪些变量分组的方式。

      假设我们有一个班级Antzilla

      class Antzilla:
          antvar = "antzilla var"
          def PrintSuperAntvar(self):
              print Antzilla.antvar
      
          def PrintInstanceOfAntvar(self):
              print self.antvar
      

      注意PrintSuperAntvar 调用基本变量,PrintInstanceOfAntvar 打印Antzilla 的实例

      如果我创建 az1 并更改 az1.antvar 它不会更改 Antzilla.antvar 值。

      az1 = Antzilla()
      az1.antvar = "new var"
      
      
      az1.PrintSuperAntvar()
      >>> antzilla var
      az1.PrintInstanceOfAntvar()
      >>> new var
      

      我现在可以使用原始起始值创建一个新的 Antzilla 实例,因为我从未更改过基类值

      az2 = Antzilla()
      az2.PrintSuperAntvar()
      >>> antzilla var
      az2.PrintInstanceOfAntvar()
      >>> antzilla var
      

      但是,如果您要更改这个超值,那么您会看到新的 Antzilla 以这个新值开头,但已经更改的 Antzilla 保持不变。

      Antzilla.antvar = "newest var"
      az3 = Antzilla()
      az3.PrintSuperAntvar()
      >>> newest var
      az3.PrintInstanceOfAntvar()
      >>> newest var
      
      az1.PrintSuperAntvar()
      >>> new var
      

      注意!!!注意当我们调用az2时会发生什么!

      az2.PrintSuperAntvar()
      >>> newest var
      

      az2 从未从 super 变量更改,所以当我们将 Antzilla.antvar"antzilla var" 更改为 "newest var" 时,az2 将继续保持 super 值。

      我们如何避免这种冲突!?很简单!

      只需向您的类添加一个构造函数,将超值复制,或将新值复制到它自己的变量中。 如果存在,当您创建一个新的Antzilla 实例时,将调用__init__ 函数

      class Antzilla:
          antvar = "antzilla var"
      
          def __init__(self):
               self.antvar = Antzilla.antvar
      
          ...
      

      您还可以将 var 作为一个要求添加到您的构造函数中,这样每个实例都是唯一的。

      class Antzilla:
          antvar = "antzilla var"
      
          def __init__(self, antvar ):
               self.antvar = antvar
      
          ...
      
      az1 = Antzilla("antzilla unique swag")
      

      但重要的是要注意,在处理列表等变量时,您需要专门为每个实例创建一个新列表。幸运的是,最好的地方也是构造函数。

      所以现在回到你的问题,.. 对于你的两个类,我会添加这样的构造函数

      对于殖民地:

      class Colony(object):
          workerAnts = 0
          list = []
          temp = []
          foodAmount = 10
      
          def __init__(self):
              self.workerAnts = 0
              self.list = []
              self.temp = []
              self.foodAmount = 10
      
          ....
      

      对于蚂蚁

      class Ant(object):  
          health = 10
          def __init__(self):
              self.health = 10
          ....
      

      最后,在出现数学或逻辑错误之前,您需要做的是用self 或给定范围内的变量名称替换所有调用基变量或超变量的位置。

      例如:

       Colony.foodAmount -= 5 
      

      改为:

       self.foodAmount -= 5
      

      ================================================ ==============================

      PS 你写的地方:

      Colony.temp.append("ant")
      

      实际上是在您的 base 列表中附加一个 string。您可能希望将其更改为 Ant.. 的构造函数,它返回 Ant 类的新实例,并使其将变量添加到 colony 的实例而不是基 colony

      self.temp.append(Ant())
      

      ================================================ ==============================

      希望这会有所帮助!

      干杯,

      卡尔

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-22
        • 1970-01-01
        • 2016-06-14
        • 2019-08-26
        • 2016-02-08
        • 2022-12-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多