I'm trying to teach myself about OOP in python and am really struggling.
I have the following:
def __init__(self, quantity):
''' '''
self.switchboard = []
self.state = False
self.quantity = quantity
for i in range(quantity):
self.switchboard.append(i)
def __str__(self):
self.on_list = []
for i in range(self.quantity):
if i == True:
self.on_list.append(i)
return("The following switches are on " + str(self.on_list))
def which_switch(self):
for i in range(len(self.switchboard)):
self.on_list = []
if self.switchboard[i] == True:
on_list.append(i)
print(on_list)
def flip(self, n):
if self.switchboard[n] == True:
self.switchboard[n] = False
else:
self.switchboard[n] = True
When I print it, I get The following switches are on [1]. Even though I put 10 in the parameters. I want it to display all the switches that are on which in this case should be zero since their initial state is 'off'.
In __str__ modify the line:
if i == True:
to:
if self.switchboard[i]:
Remember: i is just an index, what you want is to access the i-th item in switchboard!
There is also another bug, in method which_switch():
on_list.append(i)
should be:
self.on_list.append(i)
and same goes to the print line below it.
Output after the change:
The following switches are on [1, 2, 3, 4, 5, 6, 7, 8, 9]
Now, I can only guess that what you actually wanted to do in the constructor is:
def __init__(self, quantity):
''' '''
self.switchboard = []
self.state = False
self.quantity = quantity
for i in range(quantity):
self.switchboard.append(LightSwitch('off')) # create light-switch and set it to 'off'
and then, when you print them - print only the ones that are on:
def __str__(self):
self.on_list = []
for i in range(self.quantity):
if self.switchboard[i].state: # check state
self.on_list.append(i)
return("The following switches are on " + str(self.on_list))
Also the reason it's printing 1 as true, is because out of all the elements in list, it's just seeing everything as false and 1 as true (as in 0 == False, 1 == True).
You can shorten
def flip(self):
''' Sets switch to opposite position.'''
if self.state == True:
self.state = False
elif self.state == False:
self.state = True
to
def flip(self):
''' Sets switch to opposite position.'''
self.state = not self.state
and on your switchboard for
def flip_every(self, n):
for i in range(0, len(self.switchboard), n):
if self.switchboard[n] == True:
self.switchboard[n] = False
else:
self.switchboard[n] = True
to
def flip_every(self, n):
for i in range(0, self.quantity, n):
self.switchboard[n] = not self.switchboard[n]
# you could also just use self.flip(n) instead
and
def flip(self, n):
self.switchboard[n] = not self.switchboard[n]
As a nice touch: you use kind-of the same iteration in which_switch(self) and __str__(self): that operates on the indexes of switches that are on. I built a small def getOnIndexes(self): ... that returns you an int list of those indexes, and call this in both methods (DRY - dont repeat yourself principle) using a shorthand to create indexes based on a list comprehension.
def getOnIndexes(self):
return [i for i in range(self.quantity) if self.switchboard[i] == True]
In total a really nice small OOP example.
What had me stumped though, is why a Switchboard IS_A LightSwitch - I would model it slightly different, saying a SwitchBoard HAS LightSwitch'es, coming to this:
Your base class
class LightSwitch():
def __init__(self, default_state):
'''
default_state can only be 'on' or 'off'.
'''
if default_state == 'on':
self.state = True
elif default_state == 'off':
self.state = False
def turn_on(self):
self.state = True
def turn_off(self):
self.state = False
def flip(self):
self.state = not self.state
def __str__(self):
if self.state == True:
return 'I am on'
if self.state == False:
return 'I am off'
def isOn(self):
return self.state
def isOff(self):
return not self.isOn()
My alternative switch board:
class AlternateSwitchBoard():
''' '''
def __init__(self, quantity, default_state):
''' '''
self.default_state = default_state
self.switchboard = [LightSwitch(default_state) for x in range(quantity)]
self.state = False
self.quantity = quantity
def __str__(self):
retVal = ""
for i in range(self.quantity):
if self.switchboard[i].isOn():
retVal += ", On"
else:
retVal += ", Off"
return "Switchboard: " + retVal[1:].strip()
def which_switch(self):
print(self.getOnIndexes())
def flip(self, n):
self.switchboard[n].flip()
def flip_every(self,stride):
for i in range(0, self.quantity, stride):
self.switchboard[i].flip()
def reset(self):
self.switchboard = [LightSwitch(default_state) for x in range(quantity)]
def getOnIndexes(self):
return [i for i in range(self.quantity) if self.switchboard[i].isOn()]
s2 = AlternateSwitchBoard(10, "off")
s2.flip(2)
s2.flip(7)
print(str(s2))
s2.flip_every(2)
print(str(s2))
s2.which_switch()
Output:
Switchboard: Off, Off, On, Off, Off, Off, Off, On, Off, Off
Switchboard: On, Off, Off, Off, On, Off, On, On, On, Off
[0, 4, 6, 7, 8]
Related
I have the following code and it works until it gets to the 'union.set()' part. It says, "unhashable type: 'LinkedEdge' " . I am not sure why this is the case since I have looked at other sources on the web and in reference books to know that the 'g.addVertex()' method and the 'g.addEdge()' method as well as the arguments being passed should lead correctly to an output like this:
5 Vertices: A C B E D
5 Edges: A>B:3 A>C:2 B>D:1 C>D:1 D>E:2
class LinkedEdge(object):
def __init__(self, fromVertex, toVertex, weight = None):
self._vertex1 = fromVertex
self._vertex2 = toVertex
self._weight = weight
self._mark = False
def clearMark(self):
self._mark = False
def __eq__(self, other):
if self is other: return True
if type(self) != type(other):
return False
return self._vertex1 == other._vertex1 and self._vertex2 == other._vertex2
def getOtherVertex(self, thisVertex):
if thisVertex == None or thisVertex == self._vertex2:
return self._vertex1
else:
return self._vertex2
def getToVertex(self):
return self._vertex2
def getWeight(self):
return self._weight
def isMarked(self):
return self._mark
def setMark(self):
self._mark = True
def setWeight(self, weight):
self._weight = weight
def __str__(self):
return str(self._vertex1) + ">" + str(self._vertex2) + ":" + str(self._weight)
class LinkedVertex(object):
def __init__(self, label):
self._label = label
self._edgeList = []
self._mark = False
def clearMark(self):
self._mark = False;
def getLabel(self):
return self._label
def isMarked(self):
return self._mark
def setLabel(self, label, g):
g._vertices.pop(self._label, None)
g._vertices[label] = self
self._label = label
def setMark(self):
self._mark = True
def __str__(self):
return str(self._label)
def addEdgeTo(self, toVertex, weight):
edge = LinkedEdge(self, toVertex, weight)
self._edgeList.append(edge)
def getEdgeTo(self, toVertex):
edge = LinkedEdge(self, toVertex)
try:
return self._edgeList[self._edgeList.index(edge)]
except:
return None
def incidentEdges(self):
return iter(self._edgeList)
def neighboringVertices(self):
vertices = []
for edge in self._edgeList:
vertices.append(edge.getOtherVertex(self))
return iter(vertices)
def removeEdgeTo(self, toVertex):
edge = LinkedEdge(self, toVertex)
if edge in self._edgeList:
self._edgeList.remove(edge)
return True
else:
return False
class LinkedDirectedGraph(object):
def __init__(self, collection = None):
self._vertexCount = 0
self._edgeCount = 0
self._vertices = {}
if collection != None:
for label in collection:
self.addVertex(label)
# Methods for clearing, marks, sizes, string rep
def clear(self):
self._vertexCount = 0
self._edgeCount = 0
self._vertices = {}
def clearEdgeMarks(self):
for edge in self.edges():
edge.clearMark()
def clearVertexMarks(self):
for vertex in self.vertices():
vertex.clearMark()
def isEmpty(self):
return self._vertexCount == 0;
def sizeEdges(self):
return self._edgeCount
def sizeVertices(self):
return self._vertexCount
def __str__(self):
result = str(self.sizeVertices()) + " Vertices: "
for vertex in self._vertices:
result += " " + str(vertex)
result += "\n";
result += str(self.sizeEdges()) + " Edges: "
for edge in self.edges():
result += " " + str(edge)
return result
def addVertex(self, label):
self._vertices[label] = LinkedVertex(label)
self._vertexCount += 1
def containsVertex (self, label):
return label in self._vertices
def getVertex(self, label):
return self._vertices[label]
def removeVertex(self, label):
removedVertex = self._vertices.pop(label, None)
if removedVertex is None:
return False
# Examine all vertices
for vertex in self.vertices():
if vertex.removeEdgeTo(removedVertex):
self._edgeCount -= 1
self._vertexCount -= 1
return True
def addEdge(self, fromLabel, toLabel, weight):
fromVertex = self.getVertex(fromLabel)
toVertex = self.getVertex(toLabel)
fromVertex.addEdgeTo(toVertex, weight)
self._edgeCount += 1
def containsEdge(self, fromLabel, toLabel):
return self.getEdge(fromLabel, toLabel) != None
def getEdge(self, fromLabel, toLabel):
fromVertex = self._vertices[fromLabel]
toVertex = self._vertices[toLabel]
return fromVertex.getEdgeTo(toVertex)
def removeEdge (self, fromLabel, toLabel):
fromVertex = self.getVertex(fromLabel)
toVertex = self.getVertex(toLabel)
edgeRemovedFlg = fromVertex.removeEdgeTo(toVertex)
if edgeRemovedFlg:
self._edgeCount -= 1
return edgeRemovedFlg
# Iterators
def edges(self):
result = set()
for vertex in self.vertices():
edges = vertex.incidentEdges()
result = result.union(set(edges))
return iter(result)
def vertices(self):
return iter(self._vertices.values())
def incidentEdges(self, label):
return self._vertices[label].incidentEdges()
def neighboringVertices(self, label):
return self._vertices[label].neighboringVertices
g = LinkedDirectedGraph()
# Insert vertices
g.addVertex("John")
g.addVertex("Sam")
g.addVertex("Megan")
g.addVertex("Jennifer")
g.addVertex("Rick")
# Insert weighted edges
g.addEdge("John", "Sam", 3)
g.addEdge("John", "Megan", 2)
g.addEdge("Sam", "Jennifer", 1)
g.addEdge("Megan", "Jennifer", 1)
g.addEdge("Jennifer", "Rick", 2)
print(g)
If you override __eq__, then Python intentionally makes your class unhashable, since there is no longer a guarantee that the default hashing algorithm (based on the object's location in memory) is compatible with your __eq__ algorithm. "Compatible" here just means that equal objects must have equal hashes. By default, nothing is equal, so when you make some things equal using an __eq__ method, you impose a requirement on what a proper hash function must do.
If you want a custom class with a custom __eq__ method to be hashable, you must implement a __hash__ method yourself.
It could be as simple as being based on the hash of the corresponding tuple:
def __hash__(self):
return hash((type(self), self._vertex1, self._vertex2))
The Python docs explain this here.
I've noticed that the code prints the date twice to the constructor and am having trouble understanding why since I believe I only instantiate the object once within my code.
This is the constructor
def __init__(self):
self.today = date.today()
print(self.today)
Here is where I instantiate it
self.data = database()
self.schedule_today = self.data.get_bulletin()
Full code for this section of the program with some unfinished functions
class database:
sch_today = ['apt: 8:00', "breakfast", "music", 'apt: 9:00', "therapy", 'apt: 12:00', "lunch"]
test_schedule = []
def __init__(self):
self.today = date.today()
print(self.today)
def get_parse_bulletin_list(self):
temp = []
index = 0
for i in self.sch_today:
if i[0:3] == 'apt':
while index%3 != 0:
temp.append('')
index+=1
temp.append(i)
else:
temp.append(i)
index += 1
return temp
def get_bulletin(self):
n_count = 1
temp = []
ref = self.get_parse_bulletin_list()
for i in ref:
if i[0:3] == 'apt':
temp.append(paper_scrap().get_layout())
n_count = 1
elif not i:
temp.append(Notecard().blank_layout())
#elif i[0:5] == '[hlf]':
#temp.append(Notecard())
elif n_count >= 3: #allign left
temp.append(Notecard())
else:
temp.append(Notecard())
n_count += 1
return temp
def update_schedule(self):
with open('calendar.txt') as r:
pass
class BulletinInterface(RelativeLayout):
def __init__(self, **kwargs):
super(BulletinInterface, self).__init__(**kwargs)
self.data = database()
self.schedule_today = self.data.get_bulletin()
self.l1 = BulletinArea(size_hint=(1,1),
padding=(38, 135, 37, 34),
orientation=('tb-lr'))
self.add_widget(self.l1)
self.b1 = Button(text="test",
background_color=(1, 1, 1, 1),
size_hint=(0.1, 0.1)
)
self.b1.bind(on_press=self.bulletin_init)
self.add_widget(self.b1)
# bulletin board initialize
self.bulletin_init()
def bulletin_init(self, touch=None):
self.init_bulletin(self.schedule_today)
def init_bulletin(self, element_list):
for i in element_list:
self.l1.add_widget(i)
Found the problem after reviewing the construction of the GUI. The KV language and regular python code were both instantiating the GUI, leading to duplicate function calls for everything.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I am writing a Neural Net program to play connect four in python, and training it using existing minimax algorithms. I have written some basic code to establish communication between two algorithms. Following lines should result in one game between the two programs :-
game = ConnectFour()
game.start_new()
However, the game is played twice. ("IA is the winner" is printed twice.)
I added some debug print lines. There's a lot of code, but I cannot figure out the point where the problem is. So I am posting all of it. I suspect import statements, but do not know what is the problem exactly.
ConnectFourAIb.py:
from minimax2b import ConnectFour
def get_AI_move(grid):
i = 0
while grid[0][i]!=' ':
i += 1
return i
game = ConnectFour()
game.start_new()
minimax2b.py:
import os
import random
import time
from abc import ABCMeta, abstractmethod
CONNECT_FOUR_GRID_WIDTH = 7
CONNECT_FOUR_GRID_HEIGHT = 6
CONNECT_FOUR_COLORS = ["x","o"]
class ConnectFour(object):
_GRID_WIDTH = CONNECT_FOUR_GRID_WIDTH
_GRID_HEIGHT = CONNECT_FOUR_GRID_HEIGHT
_grid = None
_round = None
_finished = False
_winner = None
_current_player = None
_players = [None, None]
_COLORS = CONNECT_FOUR_COLORS
def __init__(self):
print("__init__")
self._round = 1
self._finished = False
self._winner = None
self._players[0] = _HumanPlayer(self._COLORS[0])
self._players[1] = _ComputerPlayer(self._COLORS[1])
for i in xrange(2):
print('%s play with %s ' % (self._players[i]._type, self._COLORS[i]))
self._current_player = self._players[random.randint(0, 1)]
self._grid = []
for i in xrange(self._GRID_HEIGHT):
self._grid.append([])
for j in xrange(self._GRID_WIDTH):
self._grid[i].append(' ')
def start(self):
print("start")
while not self._finished:
self._next_move()
def start_new(self):
print("start_new")
self._round = 1
self._finished = False
self._winner = None
self._current_player = self._players[random.randint(0, 1)]
self._grid = []
for i in xrange(self._GRID_HEIGHT):
self._grid.append([])
for j in xrange(self._GRID_WIDTH):
self._grid[i].append(' ')
self.start()
def _switch_player(self):
print("_switch_player")
if self._current_player == self._players[0]:
self._current_player = self._players[1]
else:
self._current_player = self._players[0]
def _next_move(self):
print("_next_move")
column = self._current_player.get_move(self._grid)
for i in xrange(self._GRID_HEIGHT - 1, -1, -1):
if self._grid[i][column] == ' ':
self._grid[i][column] = self._current_player._color
self._check_status()
if self._finished:
self._print_state()
return 1
self._switch_player()
self._round += 1
return 1
print("This column is full. Please choose an other column")
return
def _check_status(self):
print("_check_status")
if self._is_full():
self._finished = True
elif self._is_connect_four():
self._finished = True
self._winner = self._current_player
def _is_full(self):
print("_is_full")
return self._round > self._GRID_WIDTH * self._GRID_HEIGHT
def _is_connect_four(self):
print("_is_connect_four")
for i in xrange(self._GRID_HEIGHT - 1, -1, -1):
for j in xrange(self._GRID_WIDTH):
if self._grid[i][j] != ' ':
# check for vertical connect four
if self._find_vertical_four(i, j):
return True
return False
def _find_vertical_four(self, row, col):
print("_find_vertical_four")
consecutive_count = 0
if row + 3 < self._GRID_HEIGHT:
for i in xrange(4):
if self._grid[row][col] == self._grid[row + i][col]:
consecutive_count += 1
else:
break
if consecutive_count == 4:
if self._players[0]._color == self._grid[row][col]:
self._winner = self._players[0]
else:
self._winner = self._players[1]
return True
return False
def _print_state(self):
print("_print_state")
if self._finished:
print("Game Over!")
if self._winner != None:
print(str(self._winner._type) + " is the winner!")
else:
print("Game is a draw")
class _Player(object):
__metaclass__ = ABCMeta
_type = None
_color = None
def __init__(self, color):
self._color = color
#abstractmethod
def get_move(self, grid):
pass
class _HumanPlayer(_Player):
def __init__(self, color):
super(_HumanPlayer, self).__init__(color)
self._type = "Human"
def get_move(self, grid):
from ConnectFourAIb import get_AI_move
return get_AI_move(grid)
class _ComputerPlayer(_Player):
_DIFFICULTY = 5
def __init__(self, color,_DIFFICULTY=5):
super(_ComputerPlayer, self).__init__(color)
self._DIFFICULTY = _DIFFICULTY
self._type = "IA"
def get_move(self, grid):
return 4
There's still too much code to go all the way through, but at least one thing is suspicious. You start the game with a module-level call to ConnectFour in ConnectFourAIb.py. But your _HumanPlayer.get_move method imports ConnectFourAIb again; this will re-trigger the two lines at the end of that file.
To fix this, use the if __name__ == '__main__' trick:
if __name__ == '__main__':
game = ConnectFour()
game.start_new()
This ensures that those lines are only called when the file is run from the command line, not when it is imported.
(As an aside, please drop all those leading underscores. They don't add anything and just make your code harder to read.)
I would like to write a class that will return true if and only if the expression entered contains only correctly matching delimiters such as " (), <>, [], {} " What I have written thus far will do exactly that.
delim_openers = '{([<'
delim_closers = '})]>'
def check_delimiters(expr):
val = Stack()
for c in expr:
if c == (any(x) for x in delim_openers):
val.push(c)
elif c == (any(y) for y in delim_closers):
try:
val.pop()
except:
return False
return val.empty()
I am a little confused however if I were test this case against specific cases such as the one below, it'll return true when I assumed it would return False, my stack shouldn't be popping the delimiter in this case. Test case:
from unittest import TestCase
tc = TestCase()
tc.assertFalse(check_delimiters('[ ( ] )'))
tc.assertFalse(check_delimiters('((((((( ))))))'))
How might I edit the `check_delimiters' method to correctly return False in these scenarios? The stacks first-in-last-out capability tends to confuse me. Also here's the stack class code in case anything else is confusing.
class Stack:
class Node:
def __init__(self, val, next=None):
self.val = val
self.next = next
def __init__(self):
self.top = None
def push(self, val):
self.top = Stack.Node(val, self.top)
def pop(self):
assert self.top, 'Stack is empty'
val = self.top.val
self.top = self.top.next
return val
def peek(self):
return self.top.val if self.top else None
def empty(self):
return self.top == None
def __bool__(self):
return not self.empty()
def __repr__(self):
if not self.top:
return ''
return '--> ' + ', '.join(str(x) for x in self)
def __iter__(self):
n = self.top
while n:
yield n.val
n = n.next
No need for any, a simple contains-check will suffice. Then you have to check whether the closer matches the opener, otherwise you are just checking whether closers and openers are equal in numbers and that the closers never take the lead count-wise:
class Stack(list):
push = list.append
def peek(self):
return self[-1] if self else None
# ...
match = dict(zip('{([<', '})]>')) # dict makes the matching check faster
delim_openers = set(match.keys()) # set makes the 'in' check faster
delim_closers = set(match.values())
def check_delimiters(expr):
val = Stack()
for c in expr:
if c in delim_openers:
val.push(c)
elif c in delim_closers:
try:
assert match[val.pop()] == c
# fails if stack is empty or brackets don't match
except:
return False
return not val
> check_delimiters('[ ( ] )')
False
> check_delimiters('[ ( ) ]')
True
As python starter, trying to get help from smart people when encountered the problem. And that is now:
I got to compare items (Qt scene items) from one list among each other, and make separate groups of items which collides mutually.
Please help me with code :
class MainWin(QMainWindow):
def __init__(self):
super(MainWin, self).__init__()
self.Win()
self.L = self.buildAllWalls()
items = self.scene.items()
allGroups = groupItemsFromList(None, items)
self.paintGroups(allGroups)
print len(allGroups)
def paintGroups(self, groups):
for g in groups :
color = QColor(0, 0, 0)
# RANDOM COLOR
namcol = "#%s" % "".join([hex(randrange(0, 255))[2:] for i in range(3)])
color.setNamedColor(namcol)
while color.isValid() == False : # ERROR CHECK
namcol = "#%s" % "".join([hex(randrange(0, 255))[2:] for i in range(3)])
color.setNamedColor(namcol)
pen = QPen(color, 14, Qt.SolidLine)
for w in g :
w.setPen(pen)
def Win(self):
self.scene = QGraphicsScene()
self.sView = QGraphicsView(self.scene)
self.sView.setRenderHint(QPainter.Antialiasing)
self.sView.setAlignment( Qt.AlignLeft | Qt.AlignTop )
self.setCentralWidget(self.sView)
self.setGeometry(20, 380, 400, 300)
self.show()
def buildAllWalls(self):
data = self.wallCoordinates()
for p in range(len(data)) :
ptA = QPointF(data[p][0], data[p][1])
ptB = QPointF(data[p][2], data[p][3])
self.wall(ptA, ptB)
def wall(self, ptA, ptB):
pen = QPen(QColor(100, 100, 100), 14, Qt.SolidLine)
currL = self.scene.addLine(QLineF(ptA.x(), ptA.y(), ptB.x(), ptB.y()))
currL.setPen(pen)
return currL
#[50,75,325,75],
def wallCoordinates(self):
data = [[50,100,150,100],[175,200,125,200],[175,275,125,275],[175,275,175,200],
[150,150,150,100],[175,100,225,100],[250,100,325,100],[350,125,175,125],
[50,125,125,125],[125,175,125,125],[150,150,175,150],[175,150,175,200],
[50,150,100,150],[100,150,100,200],[100,200,125,200],[50,175,75,175],
[75,225,75,175],[75,225,125,225],[125,275,125,225]]
return data
def main():
app = QApplication(sys.argv)
ex = MainWin()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Here is how I would write this:
def groupItemsFromList(self, itemList):
tmp = itemList[:]
allGroups = []
while tmp:
it = tmp.pop(0)
currentGroup = [it]
# loop from back to front so we can remove items safely
for i in range(len(tmp)-1, -1, -1):
if it.collidesWithItem(tmp[i]):
currentGroup.append(tmp.pop(i))
allGroups.append(currentGroup)
return allGroups
For example:
class Test(object):
def __init__(self, key):
self.key = key
def collidesWithItem(self, other):
return isinstance(other, self.__class__) and self.key == other.key
def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, self.key)
example = [Test(1), Test(2), Test(1), Test(1), Test(3), Test(2), Test(3), Test(4)]
print groupItemsFromList(None, example)
Output:
[[Test(1), Test(1), Test(1)], [Test(2), Test(2)], [Test(3), Test(3)], [Test(4)]]
This makes the assumption that all items that collide with an item will also collide with each other.
edit: Sounds like the assumption was not valid, try the following (untested):
def groupItemsFromList(self, itemList):
tmp = itemList[:]
allGroups = []
while tmp:
it = tmp.pop(0)
currentGroup = [it]
i = len(tmp) - 1
while i >= 0:
if any(x.collidesWithItem(tmp[i]) for x in currentGroup):
currentGroup.append(tmp.pop(i))
i = len(tmp) - 1
else:
i -= 1
allGroups.append(currentGroup)
return allGroups
It looks like you could do this:
def groupItemsFromList(self, itemList):
"""
Make a list of lists, where each list is composed of the
items (excepting itself, of course) that an item collides with.
"""
return [
[item for item in itemList[:i] + itemList[i:] if item.collidesWithItem(x)]
for i, x in enumerate(itemList)
]
itemList[:i] + itemList[i:] is python idiom for "I want all elements of the original list except the i'th item."
Later: I see. You want something more like this:
def groupItemsFromList(self, itemList):
def collision_indexes(i, target):
return [i] + [j for j, item in enumerate(itemList[i + 1:], start=i + 1) if item.collidesWithItem(target)]
processed = set()
results = []
for i, target in enumerate(itemList):
if i not in processed:
indexes = collision_indexes(i, target)
processed.update(indexes)
results.append([itemList[j] for j in indexes])
return results
The only advantage here is that this is side-effect-free code. There is no mutation to the original data but only functions applied to the data and changes made to new, temporary data structures.