Python class variable referenced from within list - python

As an attempt to further my knowledge in python, I have started to create a very simple tic tac toe AI.
Currently, I am stumped at some behavior I have not expected from python where when I append a class instance variable to a local list and change the item in the local list, the instance variable will have changed too.
How can I change only the local list element without affecting the class instance variable?
This is the extract of the program which is affected:
class ticAI:
def __init__(self, board):
self.board = board
self.tic = tictactoe(board)
def calc(self):
possibilities = []
ycord = 0
for y in self.board:
xcord = 0
for x in y:
if x == 0:
possibilities.append(self.board)
possibilities[len(possibilities)-1][ycord][xcord] = 2
print(self.board)
xcord += 1
ycord += 1
self.board looks like this:
[
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]
]
and outputs this:
[[2, 0, 0], [0, 1, 0], [0, 0, 0]]
[[2, 2, 0], [0, 1, 0], [0, 0, 0]]
[[2, 2, 2], [0, 1, 0], [0, 0, 0]]
[[2, 2, 2], [2, 1, 0], [0, 0, 0]]
[[2, 2, 2], [2, 1, 2], [0, 0, 0]]
[[2, 2, 2], [2, 1, 2], [2, 0, 0]]
[[2, 2, 2], [2, 1, 2], [2, 2, 0]]
[[2, 2, 2], [2, 1, 2], [2, 2, 2]]
it should however, output this:
[[2, 0, 0], [0, 1, 0], [0, 0, 0]]
[[0, 2, 0], [0, 1, 0], [0, 0, 0]]
[[0, 0, 2], [0, 1, 0], [0, 0, 0]]
[[0, 0, 0], [2, 1, 0], [0, 0, 0]]
[[0, 0, 0], [0, 1, 2], [0, 0, 0]]
[[0, 0, 0], [0, 1, 0], [2, 0, 0]]
[[0, 0, 0], [0, 1, 0], [0, 2, 0]]
[[0, 0, 0], [0, 1, 0], [0, 0, 2]]

As made aware by #jonrsharpe, you can use deepcopy to create a copy of a variable.
Original code:
possibilities.append(self.board)
possibilities[len(possibilities)-1][ycord][xcord] = 2
print(self.board)
New code:
b = copy.deepcopy(self.board)
possibilities.append(b)
possibilities[len(possibilities)-1][ycord][xcord] = 2
print(self.board)

Related

Python 2D Array/List Assignment Gives Unwanted Result

Say, I have a 2-dimensional list/array that contains 50 subarrays that look like [0, 0]. I want to change only some parts of the array, say the first elements of each subarray from index 15 to 29. I used a for loop to do this.
array = [[0,0]] * 50
for i in range(15, 30):
array[i][0] = 1
But when I print(array), it seems like the program changes all the first elements of each subarray. Output:
[[1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0], [1, 0]]
I do not know what I am doing wrong, as the logic seems sound. Can anyone identify my mistake?
array = [[0,0]] * 50 By this, your array will have [0,0] 50 times referencing to same [0,0]. So modifying one will modify all of them.
Instead try this: array = [[0, 0] for _ in range(50)].

get lines where value does not exist

I'm trying to get from 2D list the lines where value = 2 does not exists:
[[0, 0, 1, 1],
[2, 1, 0, 2],
[2, 2, 2, 2],
[0, 0, 1, 0]]
I've been using:
for i in range(len(list)):
if list[i] in [list]:
# dot stuff
the problem here is that evrytime the code is using the actual line which is list[i] and ignore the the rest even if 2 does not exists on it
hope Iwas clear
Thank you
something like the below
lst = [[0, 0, 1, 1],
[2, 1, 0, 2],
[2, 2, 2, 2],
[0, 0, 1, 0]]
lst = [x for x in lst if 2 not in x]
print(lst)
output
[[0, 0, 1, 1], [0, 0, 1, 0]]
Is this your expected output?
[0, 0, 1, 1]
[0, 0, 1, 0]
This is the working code:
a = [[0, 0, 1, 1],
[2, 1, 0, 2],
[2, 2, 2, 2],
[0, 0, 1, 0]]
for i in range(len(a)):
if 2 not in a[i]:
print(a[i])

Appending a list to a list in a loop (Python)

I have a Python list with values, say:
a = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
I want to append in a loop a new list 'b' to the list 'a'.
b = [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]
The result should look like (when adding 'b' once to 'a'):
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
Now, I want to append the list b N times to the list a.
Both a and b have shape (4,3)
The result should then have shape: (N+1,4,3)
How do I do this?
Native Python Lists will not behave the way you expect here as described in a few comments, so if you can use a 3rd party library, consider NumPy, which will behave more like a matrix of values as you expect and can then be converted back into a Python List
Setup
>>> a = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
>>> b = [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]
Replicate b vertically
the second argument to np.tile() describes the replications in each dimension
.reshape() to prepare it as a 3-dimensional array
>>> import numpy as np
>>> b_tiled = np.tile(np.array(b), (4,1)).reshape(4,4,3)
>>> b_tiled
array([[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]]])
Collect a and b_tiled into the same array
NOTE a should be reshaped or [a] to match the shape of b_tiled
>>> np.vstack((np.array([a]), b_tiled))
array([[[0, 0, 0],
[1, 0, 1],
[1, 1, 0],
[0, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]],
[[2, 1, 0],
[3, 0, 1],
[4, 1, 0],
[2, 1, 1]]])
.tolist()
You can use .tolist() to make a native Python list again, though it may be more convenient to you as a numpy array
>>> np.vstack((np.array([a]), b_tiled)).tolist()
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]], [[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
Wrapping up my contributions to this discussion into an actual answer, summarizing what I think are the two best solutions:
1 Using NumPy arrays
Start from #Andreas' solution, but follow #ti7's lead and wrap the results into a numpy array, which manages the memory correctly:
result = np.array([a] + [b] * 5)
This solution brings the results into much more usable and versatile NumPy arrays.
2 Using deepcopy
Start from #Andreas' solution, and add a deep copy so the result does not alias parts of the array together:
import copy
result = [a] + [copy.deepcopy(b) for _ in range(5)]
This solution keeps the results as standard Python lists of lists.
Kuddos to #ti7 for noticing that the deep copy had to be done by instance of b rather than over the results, since deepcopy does not break aliasing that is internal to its input; and to #Andreas for assembling this line of code from the comments.
Caveat: since a is not deep copied here, result[0] is an alias for a, and changes to either will change both. Deep copy a too to avoid this.
add the lists and multiply the second list:
l = [a] + [b]*5
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]],
[[2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]
In case you want to modify the list values later on, be aware that you actually have 5x the SAME list referenced (as mentioned by #ti7), this means if you change one value in list b you change all, like this:
l[1][1][1] = "foo"
[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
[[2, 1, 0], [3, 'foo', 1], [4, 1, 0], [2, 1, 1]]
to avoid that use (as mentioned by #joanis):
l = [a] + [copy.deepcopy(b) for _ in range(5)]
[a+b*5] this will create
[[[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1], [2, 1, 0], [3, 0, 1], [4, 1, 0], [2, 1, 1]]]

Change all positive values in array to 1 (Python)

So I have several 3D arrays that I need to add together. Each array consists of entries with either 0 or 1. All arrays also have the same dimension. Now, when I add these arrays together some of the values overlap (which they do). However, I just need to know how the structure of the total combined array is, which means that I don't need the values 1, 2 or 3 when 2 or 3 arrays have overlapped. This also just need to be one, and of course, wherever there is a zero, the value zero just need to remain zero.
So basically what I have is:
array1 =
[[[1, 0, 0], [0, 0, 0], [0, 0, 0]],
[[0, 1, 0], [0, 0, 0], [0, 0, 0]],
[[0, 0, 1], [1, 1, 1], [0, 0, 0]]]
array2 =
[[[1, 0, 0], [0, 1, 0], [0, 0, 0]],
[[0, 0, 0], [1, 1, 0], [0, 0, 0]],
[[0, 0, 1], [0, 1, 0], [0, 0, 0]]]
So when adding them together I get:
array_total = array1 + array2 =
[[[2, 0, 0], [0, 1, 0], [0, 0, 0]],
[[0, 1, 0], [1, 1, 0], [0, 0, 0]],
[[0, 0, 2], [1, 2, 1], [0, 0, 0]]]
Where I actually want it to give me:
array_total = array1 + array2 =
[[[1, 0, 0], [0, 1, 0], [0, 0, 0]],
[[0, 1, 0], [1, 1, 0], [0, 0, 0]],
[[0, 0, 1], [1, 1, 1], [0, 0, 0]]]
So can anyone give me a hint to how this is done ?
(Assuming those are numpy arrays, or array1 + array2 would behave differently).
If you want to "change all positive values to 1", you can do this
array_total[array_total > 0] = 1
But what you actually want is an array that has a 1 where array1 or array2 has a 1, so just write it directly like that:
array_total = array1 | array2
Example:
>>> array1 = np.array([[[1, 0, 0], [0, 0, 0], [0, 0, 0]],
... [[0, 1, 0], [0, 0, 0], [0, 0, 0]],
... [[0, 0, 1], [1, 1, 1], [0, 0, 0]]])
>>> array2 = np.array([[[1, 0, 0], [0, 1, 0], [0, 0, 0]],
... [[0, 0, 0], [1, 1, 0], [0, 0, 0]],
... [[0, 0, 1], [0, 1, 0], [0, 0, 0]]])
>>> array1 | array2
array([[[1, 0, 0], [0, 1, 0], [0, 0, 0]],
[[0, 1, 0], [1, 1, 0], [0, 0, 0]],
[[0, 0, 1], [1, 1, 1], [0, 0, 0]]])

Sort a list of lists containing integers

I've got a list of lists containing integers sorted at the moment by the sum of the contents:
[[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1], [0, 2, 0], [1, 0, 1], [1, 1, 0], [2, 0, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0], [1, 0, 2], [1, 1, 1], [1, 2, 0], [2, 0, 1], [2, 1, 0], [3, 0, 0], [0, 0, 4], [0, 1, 3], [0, 2, 2], [0, 3, 1], [0, 4, 0], [1, 0, 3], [1, 1, 2], [1, 2, 1], [1, 3, 0], [2, 0, 2], [2, 1, 1], [2, 2, 0], [3, 0, 1], [3, 1, 0], [4, 0, 0]]
I would like to sort them in ascending order by the common structure of its contents i.e like
[[1, 0, 0], [2, 0, 0], [3, 0, 0], [4, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 0, 1], [0, 0, 2], [0,0,3], [0,0,4]... ]
I have seen the docs but I can't figure out how I can do this.
Is this what you're after...
>>> l = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1], [0, 2, 0], [1, 0, 1], [1, 1, 0], [2, 0, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0], [1, 0, 2], [1, 1, 1], [1, 2, 0], [2, 0, 1], [2, 1, 0], [3, 0, 0], [0, 0, 4], [0, 1, 3], [0, 2, 2], [0, 3, 1], [0, 4, 0], [1, 0, 3], [1, 1, 2], [1, 2, 1], [1, 3, 0], [2, 0, 2], [2, 1, 1], [2, 2, 0], [3, 0, 1], [3, 1, 0], [4, 0, 0]]
>>> l.sort(key=lambda x: (-x.count(0), x[::-1]))
>>> l
[[1, 0, 0], [2, 0, 0], [3, 0, 0], [4, 0, 0], [0, 1, 0], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 0, 1], [0, 0, 2], [0, 0, 3], [0, 0, 4], [1, 1, 0], [2, 1, 0], [3, 1, 0], [1, 2, 0], [2, 2, 0], [1, 3, 0], [1, 0, 1], [2, 0, 1], [3, 0, 1], [0, 1, 1], [0, 2, 1], [0, 3, 1], [1, 0, 2], [2, 0, 2], [0, 1, 2], [0, 2, 2], [1, 0, 3], [0, 1, 3], [1, 1, 1], [2, 1, 1], [1, 2, 1], [1, 1, 2]]
See http://docs.python.org/2/howto/sorting.html for an explanation of the Python sort capability, including a discussion of "Key Functions" which gives you whatever additional flexibility you need beyond the basic sort.
Use your sorting criteria function as key in the sorting.
input_list = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 2], [0, 1, 1], [0, 2, 0], [1, 0, 1], [1, 1, 0], [2, 0, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0], [1, 0, 2], [1, 1, 1], [1, 2, 0], [2, 0, 1], [2, 1, 0], [3, 0, 0], [0, 0, 4], [0, 1, 3], [0, 2, 2], [0, 3, 1], [0, 4, 0], [1, 0, 3], [1, 1, 2], [1, 2, 1], [1, 3, 0], [2, 0, 2], [2, 1, 1], [2, 2, 0], [3, 0, 1], [3, 1, 0], [4, 0, 0]]
sorted_list = sorted(input_list,key=my_sorting_func)

Categories

Resources