为什么不从我的对象列表中删除此实例?

时间:2017-12-08 01:59:40

标签: python class loops oop methods

所以我创建了两个叫做邀请和响应的类。然后,我创建了一个名为Event的类。在Event类中,我必须创建一个函数来查看当前的邀请列表和响应列表,并计算有多少邀请没有响应。

邀请和响应都有一个我在get_pending()方法中使用的“name”属性。

我有一个名为“e”的类实例,如下所示:

e = Event("graduation",[Invitation("A",5),Invitation("B",10),Invitation("C",5),Invitation("D",7)], [Response("A",True,5),Response("B",True,6),Response("C",False,0),Response("D",True,1)])
然后我打印了类方法get_pending:

print(e.count_pending())

这是我使用get_pending()方法的代码:

class Invitation:
    def __init__(self, name, num_invited):
        self.name = name #str
        self.num_invited = num_invited #positive int
    def __str__(self):
        return ("Invitation('%s', %i)" % (self.name, self.num_invited))
    def __repr__(self):
        return str(self)
    def __eq__(self, other):
        if self.name == other.name and self.num_invited == other.num_invited:
            return True
    def __lt__(self, other):
        if self.name < other.name:
            return True
        elif self.name == other.name:
            if self.num_invited < other.num_invited:
                return True
class Response:
    def __init__(self, name, ans, num_attending):
        self.name = name #str
        self.ans = ans #bool - T/F
        self.num_attending = num_attending #zero or more people
    def __str__(self):
        return ("Response('%s', %r, %i)" % (self.name, self.ans, self.num_attending))
    def __repr__(self):
        return str(self)
    def __eq__(self, other):
        if self.name == other.name and self.ans == other.ans and self.num_attending == other.num_attending:
            return True
    def __lt__(self, other):
        if self.name < other.name:
            return True
        elif self.name == other.name:
            if self.ans < other.ans:
                return True
            elif self.ans == other.ans:
                if self.num_attending < other.num_attending:
                    return True
class Event:
    def __init__(self, title, invites=None, responses=None):
        self.title = title #str
        self.invites = invites #list
        self.responses = responses #list
        if self.invites == None:
            self.invites = []
        if self.responses == None:
            self.responses = []
        self.invites.sort()
        self.responses.sort()
    def __str__(self):
        return ("""Event('%s', %r, %r)""" % (self.title, self.invites, self.responses))
    def __repr__(self):
        return str(self)
    def __eq__(self, other):
        if self.title == other.title and self.invites == other.invites and self.responses == other.responses:
            return True
    def count_pending(self):
        num_pending = 0
        lst_noresp = self.invites[:]
        for invi in lst_noresp:
            if (any(invi.name == resp.name for resp in self.responses)) == True:
                lst_noresp.remove(invi)
        for invi in lst_noresp:
            num_pending += invi.num_invited
        return num_pending

e = Event("graduation",[Invitation("A",5),Invitation("B",10),Invitation("C",5),Invitation("D",7)], [Response("A",True,5),Response("B",True,6),Response("C",False,0),Response("D",True,1)])
print(e.count_pending())

我的错误是count_pending()方法只是从邀请列表中删除了一些(邀请名为'A',邀请名为'C')的对象,即使所有邀请都有相应的响应。为什么invi.name == resp.name比较不正常?或者,这甚至是问题吗?

1 个答案:

答案 0 :(得分:1)

你有一个经典的问题,即在list进行迭代时改变它:

    for invi in lst_noresp:  # Iterating
        if (any(invi.name == resp.name for resp in self.responses)) == True:
            lst_noresp.remove(invi)  # Mutating

虽然没有记录的行为,但实际上,这会导致循环跳过每个被删除元素后面的值(迭代器存储当前索引,remove将以下元素向下移动,所以当你得到了下一个元素,你绕过了移动到被移除元素所占空间的元素),所以你甚至没有检查你的一半值。迭代self.invites,并改变lst_noresp,问题就会消失。

或者,构建新的list并避免O(n**2)效果:

    lst_noresp = [invi for invi in self.invites 
                  if not any(invi.name == resp.name for resp in self.responses)]

可以避免从list中间缓慢删除,有利于在构建新listO(n)作品)时进行过滤。