The behavior of the numpy rollaxis function confuses me.
The documentation says:
Roll the specified axis backwards, until it lies in a given position.
And for the start parameter:
The axis is rolled until it lies before this position.
To me, this is already somehow inconsistent.
Ok, straight forward example (from the documentation):
>>> a = np.ones((3,4,5,6))
>>> np.rollaxis(a, 1, 4).shape
(3, 5, 6, 4)
The axis at index 1 (4) is rolled backward till it lies before index 4.
Now, when the start index is smaller than the axis index, we have this behavior:
>>> np.rollaxis(a, 3, 1).shape
(3, 6, 4, 5)
Instead of shifting the axis at index 3 before index 1, it ends up at 1.
Why is that? Why isn't the axis always rolled to the given start index?
NumPy v1.11 and newer includes a new function, moveaxis, that I recommend using instead of rollaxis (disclaimer: I wrote it!). The source axis always ends up at the destination, without any funny off-by-one issues depending on whether start is greater or less than end:
import numpy as np
x = np.zeros((1, 2, 3, 4, 5))
for i in range(5):
print(np.moveaxis(x, 3, i).shape)
Results in:
(4, 1, 2, 3, 5)
(1, 4, 2, 3, 5)
(1, 2, 4, 3, 5)
(1, 2, 3, 4, 5)
(1, 2, 3, 5, 4)
Much of the confusion results from our human intuition - how we think about moving an axis. We could specify a number of roll steps (back or forth 2 steps), or a location in the final shape tuple, or location relative to the original shape.
I think the key to understanding rollaxis is to focus on the slots in the original shape. The most general statement that I can come up with is:
Roll a.shape[axis] to the position before a.shape[start]
before in this context means the same as in list insert(). So it is possible to insert before the end.
The basic action of rollaxis is:
axes = list(range(0, n))
axes.remove(axis)
axes.insert(start, axis)
return a.transpose(axes)
If axis<start, then start-=1 to account for the remove action.
Negative values get +=n, so rollaxis(a,-2,-3) is the same as np.rollaxis(a,2,1). e.g. a.shape[-3]==a.shape[1]. List insert also allows a negative insert position, but rollaxis doesn't make use of that feature.
So the keys are understanding that remove/insert pair of actions, and understanding transpose(x).
I suspect rollaxis is intended to be a more intuitive version of transpose. Whether it achieves that or not is another question.
You suggest either omitting the start-=1 or applying across the board
Omitting it doesn't change your 2 examples. It only affects the rollaxis(a,1,4) case, and axes.insert(4,1) is the same as axes.insert(3,1) when axes is [0,2,3]. The 1 is still placed at the end. Changing that test a bit:
np.rollaxis(a,1,3).shape
# (3, 5, 4, 6) # a.shape[1](4) placed before a.shape[3](6)
without the -=1
# transpose axes == [0, 2, 3, 1]
# (3, 5, 6, 4) # the 4 is placed at the end, after 6
If instead -=1 applies always
np.rollaxis(a,3,1).shape
# (3, 6, 4, 5)
becomes
(6, 3, 4, 5)
now the 6 is before the 3, which was the original a.shape[0]. After the roll 3 is the the a.shape[1]. But that's a different roll specification.
It comes down to how start is defined. Is a postion in the original order, or a position in the returned order?
If you prefer to think of start as an index position in the final shape, wouldn't it be simpler to drop the before part and just say 'move axis to dest slot'?
myroll(a, axis=3, dest=0) => (np.transpose(a,[3,0,1,2])
myroll(a, axis=1, dest=3) => (np.transpose(a,[0,2,3,1])
Simply dropping the -=1 test might do the trick (omiting the handling of negative numbers and boundaries)
def myroll(a,axis,dest):
x=list(range(a.ndim))
x.remove(axis)
x.insert(dest,axis)
return a.transpose(x)
a = np.arange(1*2*3*4*5).reshape(1,2,3,4,5)
np.rollaxis(a,axis,start)
'axis' is the index of the axis to be moved starting from 0. In my example the axis at position 0 is 1.
'start' is the index (again starting at 0) of the axis that we would like to move our selected axis before.
So, if start=2, the axis at position 2 is 3, therefor the selected axis will be before the 3.
Examples:
>>> np.rollaxis(a,0,2).shape # the 1 will be before the 3.
(2, 1, 3, 4, 5)
>>> np.rollaxis(a,0,3).shape # the 1 will be before the 4.
(2, 3, 1, 4, 5)
>>> np.rollaxis(a,1,2).shape # the 2 will be before the 3.
(1, 2, 3, 4, 5)
>>> np.rollaxis(a,1,3).shape # the 2 will be before the 4.
(1, 3, 2, 4, 5)
So, after the roll the number at axis before the roll will be placed just before the number at start before the roll.
If you think of rollaxis like this it is very simple and makes perfect sense, though it's strange that they chose to design it this way.
So, what happens when axis and start are the same? Well, you obviously can't put a number before itself, so the number doesn't move and the instruction becomes a no-op.
Examples:
>>> np.rollaxis(a,1,1).shape # the 2 can't be moved to before the 2.
(1, 2, 3, 4, 5)
>>> np.rollaxis(a,2, 2).shape # the 3 can't be moved to before the 3.
(1, 2, 3, 4, 5)
How about moving the axis to the end? Well, there's no number after the end, but you can specify start as after the end.
Example:
>>> np.rollaxis(a,1,5).shape # the 2 will be moved to the end.
(1, 3, 4, 5, 2)
>>> np.rollaxis(a,2,5).shape # the 3 will be moved to the end.
(1, 2, 4, 5, 3)
>>> np.rollaxis(a,4,5).shape # the 5 is already at the end.
(1, 2, 3, 4, 5)
Related
I currently have some code where I've created a mask which checks to see if a variable matches the first position in a sequence, called index_pos_overload. If it matches, the variable is chosen, and the check ends. However, I want to be able to use this mask to not only check if the number satisfies the condition of the mask, but if it doesn't move along to the next value in the sequence which does. It's essentially to pick out a row in my pandas data column, hyst. My code currently looks like this:
import pandas as pd
from itertools import chain
hyst = pd.DataFrame({"test":[12, 4, 5, 4, 1, 3, 2, 5, 10, 9, 7, 5, 3, 6, 3, 2 ,1, 5, 2]})
possible_overload_cycle = 1
index_pos_overload = chain.from_iterable((hyst.index[i])
for i in range(0, len(hyst)-1, 5))
if (possible_overload_cycle == index_pos_overload):
hyst_overload_cycle = possible_overload_cycle
else:
hyst_overload_cycle = 5 #next value in iterable where index_pos_overload is true
The expected output of hyst_overload_cycle should be this:
print(hyst_overload_cycle)
5
I've included my logic as to how I think this should work - possible_overload_cycle = 1 does not point to the first position in the dataframe, so hyst_overload_cycle should return as 5, the first position in the mask. I hope I've made sense, as I can't quite seem to work out how I would go about this programatically.
If I understood you correctly, it may be simpler than you think:
index_pos_overload can be an array / list, there is no need to use complex constructs to store a sequence of values
to find the first non-zero value from index_pos_overload, one can simply use np.nonzero()[0][0] (the first [0] is to select the dimension, the second is to select the index within that axis) and use array indexing of that on the original index_pos_overload array
The code would look like:
import numpy as np
import pandas as pd
hyst = pd.DataFrame({"test":[12, 4, 5, 4, 1, 3, 2, 5, 10, 9, 7, 5, 3, 6, 3, 2 ,1, 5, 2]})
possible_overload_cycle = 1
index_pos_overload = np.array([hyst.index[i] for i in range(0, len(hyst)-1, 5)])
if possible_overload_cycle in index_pos_overload:
hyst_overload_cycle = possible_overload_cycle
else:
hyst_overload_cycle = index_pos_overload[np.nonzero(index_pos_overload)[0][0]]
print(hyst_overload_cycle)
# 5
Justification first :)
Switch: Switch the marbles in positions 0 and 1.
Rotate: Move the marble in position 0 to position N - 1, and move all other marbles one space to the left (one index lower).
If there is a list of number (1,3,0,2)
switch - rotate - switch will sort the numbers
3,1,0,2 - 1,0,2,3 - 0,1,2,3
But if we have (3,1,0,2), it never ends with switch - rotate - switch - rotate ... method.
Is there a better way to use both switch and rotate to efficiently get the sorted result?
I cannot now think of the most efficient way (meaning the way that uses the smallest number of rotations and switches) to sort any given list. But I can think of a way, given a list, to find the most efficient way.
Consider your problem to be a breadth-first search problem in a graph data structure. Consider a list to point directly to another list if it can be gotten from the current list by a single swap or single rotation. Do a breadth-first search until the sorted list is obtained. Then the path from the original list to the sorted list is "the most efficient way." You need not actually set up the graph data structure--this just gives the algorithm's idea.
I'll try to get some specific code here soon, but here is an outline. Start with a dictionary containing only the original list (which will need to be a tuple, so I'll start calling them tuples) as a key and None as the value. This dictionary contains the "already seen tuples" as keys, and for each key the value is the tuple that lead to that key. Also start with a queue (probably Python's deque) that contains only the original tuple. This is the "seen but not yet processed" queue. Then run a loop: pop a tuple off the queue, check if it is the sorted tuple, then for each tuple reachable by a single switch or rotation check if it was already seen, add it to both the dictionary and the queue. Eventually you will reach the sorted tuple (if the original tuple was defined correctly). Use the "already seen" dictionary to print the path from the sorted tuple back to the original tuple.
Here is code based on that algorithm. Further optimizations could be done, such as in-lining the switched_or_rotated routine or checking for the target tuple when it is first seen rather than waiting for when it is processed.
from collections import deque
# Constant strings: ensure they are the same length for pretty printing
START = 'Start: '
SWITCH = 'Switch:'
ROTATE = 'Rotate:'
def switched_or_rotated(atuple):
"""Generate the tuples reachable from the given tuple by one switch
or rotation, with the action that created each tuple.
"""
yield (atuple[1::-1] + atuple[2:], SWITCH) # swap first two items
yield (atuple[1:] + atuple[:1], ROTATE) # rotate first item to the end
def sort_by_switch_and_rotate(iter):
"""Sort a finite, sortable iterable by repeatedly switching the
first two items and/or rotating it left (position 0 to the end, all
others to one index lower). Print a way to do this with the
smallest number of switches and/or rotations then return the number
of steps needed.
Based on <https://stackoverflow.com/questions/54840758/
sorting-numbers-with-mix-of-switch-and-rotate-in-python>
"""
# Initialize variables
original = tuple(iter)
targettuple = tuple(sorted(original))
alreadyseen = {original: None} # tuples already seen w/ previous tuple
actions = {original: START} # actions that got each tuple
notprocessed = deque() # tuples seen but not yet processed
# Do a breadth-first search for the target tuple
thistuple = original
while thistuple!= targettuple:
for nexttuple, nextaction in switched_or_rotated(thistuple):
if nexttuple not in alreadyseen:
alreadyseen[nexttuple] = thistuple
actions[nexttuple] = nextaction
notprocessed.append(nexttuple)
thistuple = notprocessed.popleft()
# Print the path from the original to the target
path = []
while thistuple:
path.append(thistuple)
thistuple = alreadyseen[thistuple]
print('\nHow to sort a list in {} steps:'.format(len(path)-1))
for thistuple in reversed(path):
print(actions[thistuple], thistuple)
# Return the minimal number of steps
return len(path) - 1
Here is test code for your two examples and some additional examples.
# Example tuples from the questioner
assert sort_by_switch_and_rotate((1, 3, 0, 2)) == 3
assert sort_by_switch_and_rotate((3, 1, 0, 2)) == 2
# Test tuples
assert sort_by_switch_and_rotate((0, 1, 2, 3)) == 0 # identity
assert sort_by_switch_and_rotate((1, 0, 2, 3)) == 1 # one switch
assert sort_by_switch_and_rotate((3, 0, 1, 2)) == 1 # one rotation
assert sort_by_switch_and_rotate((1, 2, 3, 0)) == 3 # max rotations
assert sort_by_switch_and_rotate((1, 0, 3, 2)) == 6 # from #MattTimmermans
The printout from that is
How to sort a list in 3 steps:
Start: (1, 3, 0, 2)
Switch: (3, 1, 0, 2)
Rotate: (1, 0, 2, 3)
Switch: (0, 1, 2, 3)
How to sort a list in 2 steps:
Start: (3, 1, 0, 2)
Rotate: (1, 0, 2, 3)
Switch: (0, 1, 2, 3)
How to sort a list in 0 steps:
Start: (0, 1, 2, 3)
How to sort a list in 1 steps:
Start: (1, 0, 2, 3)
Switch: (0, 1, 2, 3)
How to sort a list in 1 steps:
Start: (3, 0, 1, 2)
Rotate: (0, 1, 2, 3)
How to sort a list in 3 steps:
Start: (1, 2, 3, 0)
Rotate: (2, 3, 0, 1)
Rotate: (3, 0, 1, 2)
Rotate: (0, 1, 2, 3)
How to sort a list in 6 steps:
Start: (1, 0, 3, 2)
Switch: (0, 1, 3, 2)
Rotate: (1, 3, 2, 0)
Rotate: (3, 2, 0, 1)
Switch: (2, 3, 0, 1)
Rotate: (3, 0, 1, 2)
Rotate: (0, 1, 2, 3)
I don't know if this gives an answer to your question, which I found it very tricky.
I wrote a class to be used in a loop:
class Marbles:
def __init__(self, marbles):
self.marbles = marbles
self.len = len(marbles)
def switch(self):
self.marbles[0], self.marbles[1] = self.marbles[1], self.marbles[0]
if self.is_sorted(): raise StopIteration
return self
def rotate(self):
self.marbles = self.marbles[1:] + [self.marbles[0]]
if self.is_sorted(): raise StopIteration
return self
def is_sorted(self):
return all(self.marbles[i] <= self.marbles[i+1] for i in range(self.len-1))
def show(self):
print(self.marbles)
When after a move marbles are sorted it throws the exception StopIteration, so the loop can break.
So, for your example (1,3,0,2):
marbles = Marbles([1,3,0,2])
marbles.switch().show() #=> [3, 1, 0, 2]
marbles.rotate().show() #=> [1, 0, 2, 3]
marbles.switch().show() #=> StopIteration because it is sorted
Now you can write a couple of loops using brute force, where the order of actions is swapped (in this case I considered the rule to be an alternate sequence of switch and rotate):
tested = []
original = [3,1,0,2]
marbles = Marbles(original)
while True:
try:
marbles.switch().show()
marbles.rotate().show()
except: break
if original in tested: break
tested.append(marbles.marbles)
print(marbles.is_sorted())
marbles.show()
print("-"*20)
tested = []
original = [3,1,0,2]
marbles = Marbles(original)
while True:
try:
marbles.rotate().show()
marbles.switch().show()
except: break
if original in tested: break
tested.append(marbles.marbles)
print(marbles.is_sorted())
marbles.show()
This returns
# [1, 3, 0, 2]
# [3, 0, 2, 1]
# [0, 3, 2, 1]
# [3, 2, 1, 0]
# [2, 3, 1, 0]
# [3, 1, 0, 2]
# [1, 3, 0, 2]
# [3, 0, 2, 1]
# False
# [3, 0, 2, 1]
# --------------------
# [1, 0, 2, 3]
# True
# [0, 1, 2, 3]
Pick one number at the start that you will never switch to represent an unmovable start/end in the list. No matter which number you pick, your simple algorithm of switching out-of-order elements and rotating will then always work.
Note that "out of order" gets a bit complicated if you don't pick the smallest or largest element, since the correct order is cyclic. The element smaller than the ones you pick go after the larger ones.
Try all of the choices to see which one gives the fastest result.
e.g.:
Don't switch 0:
3,1,0,2 - 1,3,0,2 - 3,0,2,1 - 0,2,1,3 - 2,1,3,0 - 1,2,3,0 - 2,3,0,1 - 3,0,1,2 - 0,1,2,3
Don't switch 1:
3,1,0,2 - 1,0,2,3 - 0,2,3,1 - 2,0,3,1 - 0,3,1,2 - 3,0,1,2 - 0,1,2,3
Don't switch 2:
3,1,0,2 - 1,0,2,3 - 0,1,2,3
Don't switch 3:
3,1,0,2 - 1,0,2,3 - 0,1,2,3
EDIT: This doesn't find the best when all best solutions require all elements to participate in swaps. It does always find a solution, though, and it's polynomial time.
Python provides best way of sorting the list by using the list sort inbuilt function. For example:
my_list=[3,1,0,2]
my_list.sort()
print(my_list)
output: [0,1,2,3]
I have been trying to write code which gives the solution of the number of ways of reaching a sum, which is specified. This is very similar to the subset sums problem which I have found online (Finding all possible combinations of numbers to reach a given sum).
I modified slightly the code so that it reuses numbers multiple times.
object_list = [(2, 50), (3, 100), (5, 140)] # the first number in the tuple is my weight and the second is my cost
max_weight = 17
weight_values = [int(i[0]) for i in object_list]
cost_values = [int(i[1]) for i in object_list]
def subset_sum(objects, max_weight, weights=[]):
w = sum(weights)
if w == max_weight:
print("sum(%s)=%s" % (weights, max_weight))
if w >= max_weight:
return
for i in range(len(objects)):
o = objects[i]
subset_sum(objects, max_weight, weights + [o])
if __name__ == "__main__":
subset_sum(weight_values, max_weight)
print(subset_sum(weight_values, max_weight))
This gives the solution:
sum([2, 2, 2, 2, 2, 2, 2, 3])=17
sum([2, 2, 2, 2, 2, 2, 3, 2])=17
sum([2, 2, 2, 2, 2, 2, 5])=17
sum([2, 2, 2, 2, 2, 3, 2, 2])=17
...
So on so forth.
Unlike the original I am using a list of tuples and then taking the first value of the tuple to make a list. This is the same with the last value.
The part I am currently stuck on is how to store these values and reuse them in the next part of the code.I had a look at this post but I couldn't understand it (Python: How to store the result of an executed function and re-use later?).
So I want to store the part of the solution which stores [2, 2, 2, 2, 2, 2, 2, 3] from the solution sum([2, 2, 2, 2, 2, 2, 2, 3])=17. I want to do this for all solutions because in the next step i am going to replace the numbers with the next part of the tuple (so 2 will be replaced by 50 because the tuple that 2 is in is (2,50)). Then I am going to use this to print another sum value with the replaced numbers and print the highest value (probably going to sort the solutions from highest to lowest and print the first value in the list)
I tried using a dictionary to try and replace the values after the calculation but i couldn't manage to do it.
I tried:
dictionary = dict(zip(weight_values, cost_values))
Any help is appreciated. before anyone asks Ia have looked online for solutions and have no one else to ask help from, since the only person i know who has a background in coding is my brother who isn't at home
I want to write a search function that takes in a value x and a sorted sequence and returns the position that the value should go to by iterating through the elements of the sequence starting from the first element. The position that x should go to in the list should be the first position such that it will be less than or equal to the next element in the list.
Example:>>> search(-5, (1, 5, 10))——0
>>> search(3, (1, 5, 10))——1
Building a list of every item would be a bit of a waste of resources if there were big gaps in the list, instead you can just iterate through each list item until the input is bigger than the value.
In terms of your code -
def search(input,inputList):
for i in range( len( inputList ) ):
if inputList[i]>input:
return i
return len( inputList )
print search(-5, (1, 5, 10))
#Result: 0
print search(3, (1, 5, 10))
#Result: 1
To insert it into the list, this would work, I split the list in 2 based on the index and add the value in the middle.
def insert(input,inputList):
index = search(input,inputList) #Get where the value should be inserted
newInput = [input]+list(inputList[index:]) #Add the end of the list to the input
if index:
newInput = list(inputList[:index])+newInput #Add the start of the list if the index isn't 0
return newInput
print insert(-5, (1, 5, 10))
#Result: (-5, 1, 5, 10)
print insert(3, (1, 5, 10))
#Result: (1, 3, 5, 10)
since someone has answered a similar question, I will just draw a rough skeleton of what u may want to do.
declare a list and populate it with your stuff;
mylist = [1, 2, 3, 5, 5, 6, 7]
then just make a function and iterate the list;
def my_func( x, mylist):
for i in mylist:
if((mylist[i] == x)|| (mylist[i] > x)):
return i
Given 3 in list (1, 2, 3, 4, 5), the function should return index 2.
Given 3 in list (1, 2, 4, 5, 6), it should still return 2
You may want to check my python code, because I have not checked this for errors, I am assuming you know some python and if you have the skeleton, you should crack it. And Oh, python cares about the tabbibg I did.
In the method below, I have assembled a list of tuples, while trying to ensure that none of the values are less than zero. The method below first takes a block and uses it to calculate the coordinates of neighboring blocks in increments of one and then proceeds to remove blocks that have either of their coordinates less than zero. My issue lies in the second stage as it does not remove two coordinates if I input a block with coordinates (0,0): (0,-1) and (-1,0).
The codes is as follows:
def get_block_neighbors(self, block):
neighbor_list = []
neighbor_list.append((block.x,block.y+1))
neighbor_list.append((block.x+1,block.y+1))
neighbor_list.append((block.x+1,block.y))
neighbor_list.append((block.x+1,block.y-1))
neighbor_list.append((block.x,block.y-1))
neighbor_list.append((block.x-1,block.y-1))
neighbor_list.append((block.x-1,block.y))
neighbor_list.append((block.x-1,block.y+1))
for item in neighbor_list: #each tuple item in the list
if item[0] < 0 or item[1] < 0:
print item
neighbor_list.remove(item)
print neighbor_list
get_block_neighbors(Block(Point(0,0),"green"))
for which I get the following output:
(1, -1)
(-1, -1)
(-1, 1)
[(0, 1), (1, 1), (1, 0), (0, -1), (-1, 0)]
Here, the first three lines are printouts of the tuples I would like to remove, while the last one is a list of the tuples that I have assembled. As you can see, the last two tuples have negative values for at least one of their coordinates. Ideally, I would want this:
(1, -1)
(-1, -1)
(-1, 1)
(0, -1)
(-1, 0)
[(0, 1), (1, 1), (1, 0)]
Curiously enough, when I remove/comment out the line neighbor_list.remove(item), I get a bit closer to where I need to be in one sense in that the method in its print-out includes the two tuples that I want removed. But of course, the one disadvantage of doing this is that I no longer remove any of the target tuples from this list.
Any help on this would be much appreciated and I really do hope that my oversight wasn't something super obvious. On a side note, I feel like there should be a way to assemble this list while being able to forego a removal stage, which is how I started coding this method before I just settled for the code above. Thanks!
The issue is that you're removing items from the list at the same time you're iterating over that list. List iteration happens by index (behind the scenes) so this doesn't work as you'd expect, as some values are skipped when their predecessors are removed.
To avoid this issue, you can iterate over a copy of the list, using a slice:
for item in neighbor_list[:]:
Or, better yet, use a list comprehension to build a new list instead of modifying the old one:
new_list = [(x, y) for x, y in neighbor_list if x >= 0 and y >= 0]
You shouldn't remove items from a list you're iterating over. Make a copy of the list first, and iterate over that instead.
The problem lies in the fact that you're removing elements of a list and at the same time you're iterating over it. It's simpler if you just create a new list with the elements removed:
[item for item in neighbor_list if item[0] >= 0 and item[1] >= 0]
Make a copy of the list first:
for item in neighbor_list[:]: #each tuple item in the list
if item[0] < 0 or item[1] < 0:
print item
neighbor_list.remove(item)
print neighbor_list
Much easier all around:
def get_block_neighbors(self, block):
neighbor_list = []
neighbor_list.append((block.x,block.y+1))
neighbor_list.append((block.x+1,block.y+1))
neighbor_list.append((block.x+1,block.y))
if block.y > 0:
neighbor_list.append((block.x+1,block.y-1))
neighbor_list.append((block.x,block.y-1))
if block.x > 0:
neighbor_list.append((block.x-1,block.y-1))
if block.x > 0:
neighbor_list.append((block.x-1,block.y))
neighbor_list.append((block.x-1,block.y+1))
return neighbor_list
Here is an example, showing some code that looks like it's trying to filter some numbers from a list
>>> L = range(10)
>>> for x in L:
... print x, L
... if x in (4,5,6,7):
... L.remove(x)
...
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
6 [0, 1, 2, 3, 5, 6, 7, 8, 9]
8 [0, 1, 2, 3, 5, 7, 8, 9]
9 [0, 1, 2, 3, 5, 7, 8, 9]
Removing items while iterating over the list is generally a bad idea.
Here is a simpler way to get the list of neighbors. It avoids the need for .remove altogether
def get_block_neighbors(self, block):
x = block.x
y = block.y
xrange = (-1, 0, 1)[x<1:]
yrange = (-1, 0, 1)[y<1:]
return [(x + dx,y + dy) for dx in xrange for dy in yrange if dx or dy]