Bernheart
Bernheart

Reputation: 637

Append() applied to a list goes into loop

I'm Python newbie, and, as you can see, I'm trying to study Object programming. In the next code, I'm trying to model trucks that transport items: every item has an identifier and a weight. Tracks have max weight capacity. Look at the code below, than I will tell you the problem.

class Item():
    '''
    Class Item models an object transported in a supply chain
    '''
    def __init__(self, id, weight):
        '''
        Constructor of an object of class Item
        :param id: Unique identifier of the Item
        :param weight: the weight of the Item, expressed in Kg
        '''

        self.id = id
        self.weight = weight

class Truck():
    '''
    Class Truck models a truck transporting Items
    '''

    def __init__(self, plate_number, capacity):
        '''
        Constructor of an object of class Truck
        :param plate_number: Unique identifier of the Truck
        :param capacity: maximum weight transportable by the Truck
        '''
        self.plate_number = plate_number
        self.capacity = capacity
        self.remaining_capacity = capacity

    def load(self, items):
        '''
        This method allows to load a list of items into a truck if their
        total weight is less that or equal to the remaining capacity of the truck.
        If this is case, True is returned. Otherwise False is returned
        :param items:
        :return:
        '''

        total_load = 0
        for item in items:
            total_load += item.weight

            if total_load > self.remaining_capacity:
            print('out of range')
            return False
        else:
            for item in items:
                items.append(item)
                self.remaining_capacity = self.remaining_capacity - item.weight
            return True

    a = Item('AAA01', 10)
    b = Item('BBB01', 20)
    c = Item('CCC01', 30)

    truck_a = Truck('LE000A', 35)
    truck_a.load([a, b])

    print('Remaining capcity of Truck ', truck_a.plate_number, ' is ',truck_a.remaining_capacity)

In the load(items) method of Trucks, you can see the command items.append(item): if that command is delated, the averall program works; if not, it goes in loop, but I can't image why.

EDIT Using the next code, it seems to work, without changing the items.append(item) statement:

class Item():
'''
Class Item models an object transported in a supply chain
'''
def __init__(self, id, weight):
    '''
    Constructor of an object of class Item
    :param id: a unique code associated to the item
    :param weight: the weight of the item expressed in kg
    '''
    self.id = id
    self.weight = weight
class Truck():
'''
This class models a truck
'''
def __init__(self, plate_number, capacity, items):
    '''
    Clonstructor of an object of class Truck
    :param plate_number: a unique number Identifying the truck
    :param capacity: maximum weight that the truck can carry
    :param items: list of objects of class Item carried by the truck
    '''
    self.plate_number = plate_number
    self.capacity = capacity
    self.items = list(items)
    self.remaining_capacity = capacity
    for item in self.items:
            self.remaining_capacity = self.remaining_capacity - item.weight

def load(self, items):
    '''
    This method allows to load a list of items into a truck if their
    total weight is less that or equal to the remaining capacity of the truck.
    If this is case, True is returned. Otherwise False is returned
    :param items:
    :return:
    '''
    total_load = 0
    for item in items:
        total_load = item.weight
    if total_load > self.remaining_capacity:
        return False
    else:
        for item in items:
            self.items.append(item)
            self.remaining_capacity = self.remaining_capacity - item.weight
            return True

a = Item('AAA01', 10)
b = Item('BBB01', 20)
c = Item('CCC01', 30)

truck_a = Truck('LE000A', 35, [a])
truck_a.load([b])

print('Remaining capacity of Truck ', truck_a.plate_number, ' is ', truck_a.remaining_capacity)

As you can see, main difference in the EDIT code is that class Truck has a constructor that takes items as parameter and make a "copy" of it with the self.items = list(items). I don't know why this last code has been working

Upvotes: 0

Views: 784

Answers (3)

TemporalWolf
TemporalWolf

Reputation: 7952

It appears to me like you're trying to keep a record of what is on the truck... you need to add a self.items attribute and then append items to that:

def __init__(self, plate_number, capacity):
    ...
    self.items = []  # Items on the truck

Then in load() you can add items via self.items.append():

else:
    for item in items:
        self.items.append(item)
        self.remaining_capacity = self.remaining_capacity - item.weight
    return True

If you compare the runs of the following function on your first and second attempts, you'll see why:

def attributes(obj):
    return [s for s in dir(obj) if s[0] != '_']

print attributes(truck_a)

in the first case, it won't have any items list, because you didn't assign it one (self.items). Once you do, it then has an attribute items which can be accessed in the class via self.items. This is different from the passed in parameter items. You can make it less confusing by using different names:

def load(self, items_to_load):
    ...
    for item_to_load in items_to_load:
        self.items.append(item_to_load)

Upvotes: 2

Isaac Saffold
Isaac Saffold

Reputation: 1185

As mentioned in the previous comment and answer, you're iterating over the items in a list to which an item is being added each iteration, thereby generating an infinite loop. items += items will work in this case, as mentioned. The general problem you're facing, however, is how to alter the items in a list while iterating over them. The best way is to iterate over a copy of the list, like so:

>>> items = [1, 2, 3]
>>> for item in items[:]:
        items.append(item)


>>> items
[1, 2, 3, 1, 2, 3]
>>> 

items[:] is a slice of items consisting of all members of items, which is by definition a copy of items. For dictionaries, which you cannot slice, use the copy() method.

Upvotes: 1

Prune
Prune

Reputation: 77850

Your for loop alters the Instead, try this: list while it's iterating through the list. You can't ever get to the end, because you keep extending it while you work on it. What are you trying to do with the list items? If all you want is a list with the elements doubled, you can use

items += items

Here's a short illustration:

items = ["hello", 17, True]

for item in items:
    items.append(item)
    print items

Output:

['hello', 17, True, 'hello']
['hello', 17, True, 'hello', 17]
['hello', 17, True, 'hello', 17, True]
['hello', 17, True, 'hello', 17, True, 'hello']
['hello', 17, True, 'hello', 17, True, 'hello', 17]
['hello', 17, True, 'hello', 17, True, 'hello', 17, True]
...

Upvotes: 0

Related Questions