简体   繁体   中英

Why isn't this instance being removed from my list of objects?

So I have created 2 classes called Invitation and Response. Then, I created a class called Event. Within the Event class, I have to create function that looks at the current list of invites and list of responses and counts how many invites have no response.

The invites and the responses both have a "name" attribute that I use in my get_pending() method.

I have a class instance called "e" that looks as follows:

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)])

I then print the class method get_pending:

print(e.count_pending())

Here is my code with the get_pending() method:

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())

My error is that the count_pending() method is only removing some (Invite with name 'A' and Invite with name 'C') objects from the invite list even though all invites have a corresponding response. Why isn't the invi.name == resp.name comparison working properly? Or, is that even the issue?

You have the classic problem of mutating a list while iterating it here:

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

While there is no documented behavior for it, in practice, this causes the loop to skip the value following each removed element (the iterator stores the current index, and the remove shifts the following elements down, so when you get the next element, you've bypassed the element that was shifted into the space occupied by the removed element), so you're not even checking half your values. Iterate over self.invites , and mutate lst_noresp , and the problem should disappear.

Alternatively, construct a new list and avoid O(n**2) performance:

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

which avoids slow removals from the middle of a list in favor of filtering during construction of a new list ( O(n) work).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM