2025-10-01
__init__ method)Array class or another array-based collection, such as Python’s list type__init__ method must deal with the possibility that its caller has provided a source collection parameter:
ArrayBag objectStep 1: Choose and Initialize the Data Structures
baginterface.py, and rename it to arraybag.pyArrayBag, add a class variable for the default capacity, and complete the __init__ method"""
File: arraybag.py
"""
from arrays import Array
class ArrayBag(object):
"""An array-based bag implementation."""
# Class variable
DEFAULT_CAPACITY = 10
# Constructor
def __init__(self, sourceCollection = None):
"""Sets the initial state of self, which includes the
contents of sourceCollection, if it’s present."""
self.items = Array(ArrayBag.DEFAULT_CAPACITY)
self.size = 0
if sourceCollection:
for item in sourceCollection:
self.add(item)IsEmpty, _len_, clear, and add# Accessor methods
def isEmpty(self):
"""Returns True if len(self) == 0, or False otherwise."""
return len(self) == 0
def __len__(self):
"""Returns the number of items in self."""
return self.size
# Mutator methods
def clear(self):
"""Makes self become empty."""
self.size = 0
self.items = Array(ArrayBag.DEFAULT_CAPACITY)
def add(self, item):
"""Adds item to self."""
# Check array memory here and increase it if necessary
self.items[len(self)] = item
self.size += 1Step 2:
self to get something done within a class definition, whenever possiblelen(self) instead of referring directly to the instance variable self.sizeadd method
add method places the new item at the logical end of the arrayStep 3:
__iter__ method maintains a cursor that allows it to navigate through a sequence of objects:
__iter__ method’s while loop terminatesdef __iter__(self):
"""Supports iteration over a view of self."""
cursor = 0
while cursor < len(self):
yield self.items[cursor]
cursor += 1yield keyword in Python is used in a function to make it a generator.yield allows you to use the object in a for loop, and each iteration retrieves the next item from the collection.__eq__ method follows the rules for the equality test discussed in Chapter 2__add__ method follows the rules for the concatenation of two collections discussed in Chapter 2__str__ method uses the map and join operations to build a string containing the string representations of the bag’s itemsdef __str__(self):
"""Returns the string representation of self."""
return "{" + ", ".join(map(str, self)) + "}"
# possible to use [] to wrap the collection instead of {} which is a set notation
def __add__(self, other):
"""Returns a new bag containing the contents
of self and other."""
result = ArrayBag(self)
for item in other:
result.add(item)
return result
def __eq__(self, other):
"""Returns True if self equals other,
or False otherwise."""
if self is other: return True
if type(self) != type(other) or \
len(self) != len(other):
return False
for item in self:
if self.count(item) != other.count(item):
return False
return Trueremove Methodremove method is the most challenging method to complete in the bag implementationremove Methoddef remove(self, item):
"""Precondition: item is in self.
Raises: KeyError if item in not in self.
postcondition: item is removed from self."""
# 1. check precondition and raise an exception if necessary
if not item in self:
raise KeyError(str(item) + " not in bag")
# 2. Search for index of target item
targetIndex = 0
for targetItem in self:
if targetItem == item:
break
targetIndex += 1
# 3. Shift items to the right of target left by one position
for i in range(targetIndex, len(self) - 1):
self.items[i] = self.items[i + 1]
# 4. Decrement logical size
self.size -= 1
# 5. Check array memory here and decrease it if necessarybaginterface.pyArrayBag class, without changes to the LinkedBag classLinkedBag class are those that cannot avoid this direct access to data:__init____iter__clearaddremove__init__ method in LinkedBag is to create the instance variables and give them initial valuesself.items is now an external pointer instead of an arrayNone, the state of an empty linked structureelf.items refers to the first node in the linked structure"""
File: linkedbag.py
"""
from node import Node
class LinkedBag(object):
"""A link-based bag implementation."""
# Constructor
def __init__(self, sourceCollection = None):
"""Sets the initial state of self, which includes the
contents of sourceCollection, if it’s present."""
self.items = None
self.size = 0
if sourceCollection:
for item in sourceCollection:
self.add(item)Iterator__iter__ method for LinkedBag supports the same kind of traversal as it does in ArrayBag:
clear and addclear in LinkedBag is quite similar to its sister method in ArrayBagadd in LinkedBag leverages constant-time access by placing the new item at the head of the linked structureremove Methodremove in LinkedBag must first handle the precondition and then do a sequential search for the target itemelf.items to this node’s next linkremove Methoddef remove(self, item):
"""Precondition: item is in self.
Raises: KeyError if item is not in self.
Postcondition: item is removed from self."""
# Check precondition and raise an exception if necessary
if not item in self:
raise KeyError(str(item) + " not in bag")
# Search for the node containing the target item
# probe will point to the target node, and trailer
# will point to the node before it, if it exists
probe = self.items
trailer = None
for targetItem in self:
if targetItem == item:
break
trailer = probe
probe = probe.next
# Unhook the node to be deleted, either the first one or
# one thereafter
if probe == self.items:
self.items = self.items.next
else:
trailer.next = probe.next
# Decrement logical size
self.size -= 1
remove operations take linear time in both implementations, because they incorporate a sequential searchremove operation in ArrayBag must do the additional work of shifting data items in the array, but the cumulative effect is not worse than linear+, str, and iter operations are linear== operation has several cases and is left as an exerciseArrayBag’s add incurs an occasional linear time hit to resize the arrayArrayBag is better than half full,
LinkedBag of the same logical sizeLinkedBag uses twice as much memory as an ArrayBag whose array is fullArrayBag are generally slower than they are on a LinkedBag"""
File: testbag.py
A tester program for bag implementations.
"""
from arraybag import ArrayBag
from linkedbag import LinkedBag
def test(bagType):
"""Expects a bag type as an argument and runs some tests
on objects of that type."""
print("Testing", bagType)
lyst = [2013, 61, 1973]
print("The list of items added is:", lyst)
b1 = bagType(lyst)
print("Length, expect 3:", len(b1))
print("Expect the bag’s string:", b1)
print("2013 in bag, expect True:", 2013 in b1)
print("2012 in bag, expect False:", 2012 in b1)
print("Expect the items on separate lines:")
for item in b1:
print(item)
b1.clear()
print("Clearing the bag, expect {}:", b1)
b1.add(25)
b1.remove(25)
print("Adding and then removing 25, expect {}:", b1)
b1 = bagType(lyst)
b2 = bagType(b1)
print("Cloning the bag, expect True for ==:", b1 == b2)
print("Expect False for is:", b1 is b2)
print("+ the two bags, expect two of each item:", b1 + b2)
for item in lyst:
b1.remove(item)
print("Remove all items, expect {}:", b1)
print("Removing nonexistent item, expect crash with KeyError:")
b2.remove(99)