Python 为什么这段代码只工作了一半的时间?

Python 为什么这段代码只工作了一半的时间?,python,python-3.x,Python,Python 3.x,我有以下测试 def test_employees_not_arround_for_more_than_3_rounds(self): self.game_object.generate_workers() people_in_list_turn_1 = self.game_object.employees[:] self.game_object.next_turn() self.game_object.generate_workers() self.g

我有以下测试

def test_employees_not_arround_for_more_than_3_rounds(self):
    self.game_object.generate_workers()

    people_in_list_turn_1 = self.game_object.employees[:]
    self.game_object.next_turn()
    self.game_object.generate_workers()
    self.game_object.next_turn()
    self.game_object.generate_workers()
    self.game_object.next_turn()

    for employee in people_in_list_turn_1:
        self.assertFalse(employee in self.game_object.employees)
基本上,它会生成一个随机数目的工作人员,并将其添加到我的
game\u object.employees
列表中。 当我调用
game\u对象。next\u turn
函数时,每个员工都有一个
turns\u unemployee
变量,该变量保存他们失业的轮数,一旦达到3,工人将从
game\u对象中删除。员工将全部从
列表中删除

下面是game_object.py中的实现代码:

def generate_workers(self):
    workersToAdd = range(random.randrange(1,8))
    for i in workersToAdd:
        self.__employees.append(Employee())

def next_turn(self):
    self.__current_turn += 1
    self.__call_employees_turn_function()
    self.__remove_workers_unemployed_for_3_rounds()

def __call_employees_turn_function(self):
    for employee in self.employees:
        employee.turn()

def __remove_workers_unemployed_for_3_rounds(self):
    for employee in self.employees:
        if employee.turns_unemployed >= 3:
            self.employees.remove(employee)
我已经做了一个测试,检查调用
employee.turn()
turns\u
变量是否实际增加了1,所以我知道这是可行的

真正困扰我的是,我的测试只有50%的时间可以运行,我不知道为什么。。。有人看到任何可能导致差异的东西吗


顺便说一句,运行Python3.2.2时,您正在从列表中删除项目,同时在
\uuuu remove\uworkers\uunemployee\u3\urounds
中对其进行迭代,因此循环会跳过您希望删除的项目。您需要迭代列表的一个副本

def __remove_workers_unemployed_for_3_rounds(self):
    for employee in self.employees[:]:
        if employee.turns_unemployed >= 3:
            self.employees.remove(employee)
例如:


每轮产生2名新员工。在第四轮,您有2名员工要删除(列表中的前两名)。开始迭代并删除第一个。该列表现在只有五项,但迭代将继续并查看第二项。问题是,第二项不再是第二名员工,而是第三名员工。第二名员工将保留在列表中,您的测试将失败。只有在第一轮只生成一名员工的情况下,您的测试才会起作用。

Hugo关于导致您出现问题的原因可能是正确的;在迭代列表时,不能从列表中删除项目。然而,还有一个可能的问题,当你创建员工时,你把他们放在一个名为“员工”的列表中,即

def generate_workers(self):
    workersToAdd = range(random.randrange(1,8))
    for i in workersToAdd:
        self.__employees.append(Employee())
def __call_employees_turn_function(self):
    for employee in self.employees:
        employee.turn()

def __remove_workers_unemployed_for_3_rounds(self):
    for employee in self.employees:
        if employee.turns_unemployed >= 3:
            self.employees.remove(employee)
但当你稍后迭代它们时,你使用的是一个名为employees的列表,即

def generate_workers(self):
    workersToAdd = range(random.randrange(1,8))
    for i in workersToAdd:
        self.__employees.append(Employee())
def __call_employees_turn_function(self):
    for employee in self.employees:
        employee.turn()

def __remove_workers_unemployed_for_3_rounds(self):
    for employee in self.employees:
        if employee.turns_unemployed >= 3:
            self.employees.remove(employee)

但我不知道这是否与您的问题有关,因为我看不到您的其余代码-我甚至不确定这些代码是否在同一个类中。您可能应该发布出现问题的最小完整代码段,这样人们就可以实际运行您的代码并自己重现问题。

不要修改您正在迭代的容器

保留一个副本以进行迭代也是一个难看的技巧,并且在以后的情况下,如果您必须对对象标识与对象相等进行精确的比较,它可能会使您陷入困境。它也很凌乱

有一种更简单的方法:采用函数式编程方法。使用规则“原始容器中不符合删除条件的所有内容”创建一个新容器,然后开始使用该容器而不是原始容器

def __remove_workers_unemployed_for_3_rounds(self):
    self.employees = filter(lambda e: e.turns_unemployed < 3, self.employees)
    # Or with a list comprehension:
    # self.employees = [e for e in self.employees if e.turns_unemployed < 3]
    # if you find that more readable.
def u移除工人u失业u进行3轮(自我):
self.employees=filter(lambda e:e.e.u<3,self.employees)
#或使用列表:
#self.employees=[e表示self.employees中的e,如果e.失业<3]
#如果你觉得更可读的话。

拼写:
周围
->
周围
从代码清晰的角度来看,我建议您将
\uu呼叫员工\uu转身功能
\uu解雇工人\uu失业\uu进行3轮
插入
下一轮
。他们都很矮。只需添加一个描述性注释,如
#删除失业工人3轮
。此外,
(删除失业工人3轮
)非常具体。如果你以后改变规则,让失业工人在四轮轮换中留下来呢?我认为在这种情况下,如果您打算将其作为一个单独的方法使用,那么最好使用一个不太具体(且较短)的名称。也许类似于
解除长期失业
。甚至可能是
purge\u tramps
@DannyMilosavljevic:一点也不。代码流越简单,代码就越容易阅读。将两行和三行拆分为单独的函数会使代码更难阅读<代码>\呼叫\员工\转弯\功能
?这两条线本身就是清晰的<代码>\u解雇\u工人\u失业\u三轮
?如果你觉得特别需要评论,把评论放进去。但在没有必要的情况下,不要将其拆分为单独的方法。(我还假设这些函数不会从其他任何地方调用。如果是的话,添加一个类似的方法可能是值得的。)@Robinhegglundhansen:正如我所说,“如果你觉得特别需要评论的话。”我会考虑正在发生的事情非常明显,因此可能不会在那里发表评论。向你提示你发布足够的代码,其他人为自己运行的代码。不过,要解释一下你的困惑:员工是一种具有返回员工的getter的属性。不过,我很高兴您指出了这一点,因为在我的代码中使用employees而不是u employees是一个一致性错误,谢谢!:我不明白为什么这是一个更好的解决方案?基本上,您只需执行额外的步骤,即创建一个预筛选的要从中删除的对象列表。也不理解您关于对象标识与相等的论点,因为在这两种情况下,您都将迭代对相同对象的引用。。。我不是在试图听起来/显得粗鲁,只是想理解为什么这样更好?我不是在“创建一个预筛选的对象列表以从中删除”;过滤将删除不需要的对象。在一个表达式中,您可以得到所需的结果,然后只需将其分配回属性即可开始使用它。没有“额外步骤”。您看到的代码就是整个过程。简单总比复杂好。我正在与其他人进行手工操作,实际上我现在不知道这会在哪里引起问题,但迭代引用的副本仍然很难看。我喜欢这样——避免了原始问题并实现了