287

I'm currently working on a map editor for a game in pygame, using tile maps. The level is built up out of blocks in the following structure (though much larger):

level1 = (
         (1,1,1,1,1,1)
         (1,0,0,0,0,1)
         (1,0,0,0,0,1)
         (1,0,0,0,0,1)
         (1,0,0,0,0,1)
         (1,1,1,1,1,1))

where "1" is a block that's a wall and "0" is a block that's empty air.

The following code is basically the one handling the change of block type:

clicked = pygame.mouse.get_pressed()
if clicked[0] == 1:
    currLevel[((mousey+cameraY)/60)][((mousex+cameraX)/60)] = 1

But since the level is stored in a tuple, I'm unable to change the values of the different blocks. How do I go about changing the different values in the level in an easy manner?

codeforester
  • 39,467
  • 16
  • 112
  • 140
user2133308
  • 2,871
  • 2
  • 13
  • 4
  • 16
    don't use a tuple, just use a list from the beginning. It might really slow down your code if your level is huge, if you have to keep converting them – jamylak Apr 30 '13 at 09:36
  • 4
    how about going with lists instead of tuples from the very beginning? – Krzysztof Bujniewicz Apr 30 '13 at 09:37
  • 7
    @user2133308 btw just a compatibility note, you should use integer division `//` instead of just `/` because in Python 3, `/` will perform floating point division and screw up your code. – jamylak Apr 30 '13 at 09:57

11 Answers11

376

Convert tuple to list:

>>> t = ('my', 'name', 'is', 'mr', 'tuple')
>>> t
('my', 'name', 'is', 'mr', 'tuple')
>>> list(t)
['my', 'name', 'is', 'mr', 'tuple']

Convert list to tuple:

>>> l = ['my', 'name', 'is', 'mr', 'list']
>>> l
['my', 'name', 'is', 'mr', 'list']
>>> tuple(l)
('my', 'name', 'is', 'mr', 'list')
Khonix
  • 4,128
  • 1
  • 16
  • 15
  • 10
    This is not working for me. If I run the code in the first block to convert the tuple t to a list by passing t to list(), I get an error message: "*** Error in argument: '(t)'" This seems to happen to me only while debugging. Still confused. – Jimmy Aug 28 '18 at 19:30
  • 15
    @Jimmy that's because list is a debugger command, run `p list(...)` instead. – moritz Jan 24 '19 at 02:26
89

You have a tuple of tuples.
To convert every tuple to a list:

[list(i) for i in level] # list of lists

--- OR ---

map(list, level)

And after you are done editing, just convert them back:

tuple(tuple(i) for i in edited) # tuple of tuples

--- OR --- (Thanks @jamylak)

tuple(itertools.imap(tuple, edited))

You can also use a numpy array:

>>> a = numpy.array(level1)
>>> a
array([[1, 1, 1, 1, 1, 1],
       [1, 0, 0, 0, 0, 1],
       [1, 0, 0, 0, 0, 1],
       [1, 0, 0, 0, 0, 1],
       [1, 0, 0, 0, 0, 1],
       [1, 1, 1, 1, 1, 1]])

For manipulating:

if clicked[0] == 1:
    x = (mousey + cameraY) // 60 # For readability
    y = (mousex + cameraX) // 60 # For readability
    a[x][y] = 1
pradyunsg
  • 18,287
  • 11
  • 43
  • 96
  • 2
    NumPy is the fundamental package for scientific computing with Python. NumPy's main object is the homogeneous multidimensional array. It is a table of elements (usually numbers), all of the same type, indexed by a tuple of positive integers. – pradyunsg Dec 04 '16 at 03:25
27

You can have a list of lists. Convert your tuple of tuples to a list of lists using:

level1 = [list(row) for row in level1]

or

level1 = map(list, level1)

and modify them accordingly.

But a numpy array is cooler.

eumiro
  • 207,213
  • 34
  • 299
  • 261
24

Since Python 3.5 (PEP 448 -- Additional Unpacking Generalizations) one can use the following literal syntax to convert a tuple to a list:

>>> t = (1,2,3)
>>> lst = [*t]
>>> lst
[1, 2, 3]
>>> *lst,  # back to tuple
(1, 2, 3)

A list comprehension can be use to convert a tuple of tuples to a list of lists:

>>> level1 = (
...      (1,1,1,1,1,1),
...      (1,0,0,0,0,1),
...      (1,0,0,0,0,1),
...      (1,0,0,0,0,1),
...      (1,0,0,0,0,1),
...      (1,1,1,1,1,1))
>>> level1_list = [[*row] for row in level1]
>>> level1_list
[[1, 1, 1, 1, 1, 1], 
 [1, 0, 0, 0, 0, 1], 
 [1, 0, 0, 0, 0, 1], 
 [1, 0, 0, 0, 0, 1], 
 [1, 0, 0, 0, 0, 1], 
 [1, 1, 1, 1, 1, 1]]
>>> *((*row,) for row in level1_list),
((1, 1, 1, 1, 1, 1), 
 (1, 0, 0, 0, 0, 1), 
 (1, 0, 0, 0, 0, 1), 
 (1, 0, 0, 0, 0, 1), 
 (1, 0, 0, 0, 0, 1), 
 (1, 1, 1, 1, 1, 1))
>>> _ == level1
True
AXO
  • 8,198
  • 6
  • 62
  • 63
20

To convert tuples to list

(Commas were missing between the tuples in the given question, it was added to prevent error message)

Method 1:

level1 = (
     (1,1,1,1,1,1),
     (1,0,0,0,0,1),
     (1,0,0,0,0,1),
     (1,0,0,0,0,1),
     (1,0,0,0,0,1),
     (1,1,1,1,1,1))

level1 = [list(row) for row in level1]

print(level1)

Method 2:

level1 = map(list,level1)

print(list(level1))

Method 1 took --- 0.0019991397857666016 seconds ---

Method 2 took --- 0.0010001659393310547 seconds ---

Aravind Krishnakumar
  • 2,727
  • 1
  • 28
  • 25
16

Why don't you try converting its type from a tuple to a list and vice versa.

level1 = (
     (1,1,1,1,1,1)
     (1,0,0,0,0,1)
     (1,0,0,0,0,1)
     (1,0,0,0,0,1)
     (1,0,0,0,0,1)
     (1,1,1,1,1,1))

print(level1)

level1 = list(level1)

print(level1)

level1 = tuple(level1)

print(level1)
5

Both the answers are good, but a little advice:

Tuples are immutable, which implies that they cannot be changed. So if you need to manipulate data, it is better to store data in a list, it will reduce unnecessary overhead.

In your case extract the data to a list, as shown by eumiro, and after modifying create a similar tuple of similar structure as answer given by Schoolboy.

Also as suggested using numpy array is a better option

tanzil
  • 1,461
  • 2
  • 12
  • 17
  • You should also write in this answer, that `numpy` will provide the fastest solution to working with this type of data. – jamylak Apr 30 '13 at 09:59
  • Of course you can use immutable data structures such as tuples even when you are manipulating data. The whole premise of functional programming and all that largely builds on persistence of data. But of course, in Python land you may want to go with the masses and mutate freely... – mike3996 Aug 26 '14 at 12:26
5

List to Tuple and back can be done as below

import ast, sys
input_str = sys.stdin.read()
input_tuple = ast.literal_eval(input_str)

l = list(input_tuple)
l.append('Python')
#print(l)
tuple_2 = tuple(l)

# Make sure to name the final tuple 'tuple_2'
print(tuple_2)
CDspace
  • 2,639
  • 18
  • 30
  • 36
2

You could dramatically speed up your stuff if you used just one list instead of a list of lists. This is possible of course only if all your inner lists are of the same size (which is true in your example, so I just assume this).

WIDTH = 6
level1 = [ 1,1,1,1,1,1,
           1,0,0,0,0,1,
           1,0,0,0,0,1,
           1,0,0,0,0,1,
           1,0,0,0,0,1,
           1,1,1,1,1,1 ]
print level1[x + y*WIDTH]  # print value at (x,y)

And you could be even faster if you used a bitfield instead of a list:

WIDTH = 8  # better align your width to bytes, eases things later
level1 = 0xFC84848484FC  # bit field representation of the level
print "1" if level1 & mask(x, y) else "0"  # print bit at (x, y)
level1 |= mask(x, y)  # set bit at (x, y)
level1 &= ~mask(x, y)  # clear bit at (x, y)

with

def mask(x, y):
  return 1 << (WIDTH-x + y*WIDTH)

But that's working only if your fields just contain 0 or 1 of course. If you need more values, you'd have to combine several bits which would make the issue much more complicated.

Alfe
  • 56,346
  • 20
  • 107
  • 159
2

Just using the command list did not work for me.

if you have a tuple just iterate until you have the elements there are necessary and after that append to a list. And if you go to the element level you can change it easily.

input:

level1 = (
         (1,1,1,1,1,1),
         (1,0,0,0,0,1),
         (1,0,0,0,0,1),
         (1,0,0,0,0,1),
         (1,0,0,0,0,1),
         (1,1,1,1,1,1))

level1_as_list=[]
for i in level1:
    inside_list=[]
    for j in i:
        inside_list.append(j)
    level1_as_list.append(inside_list)

print(level1_as_list)enter code here

output:

[[1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1]]
1

In my case, I wanted to merge rows/ lists:

a = ['Tom', 'Dick', 'Harry']
b = ['20', '32', '14']
c = ['foo', 'bar', 'blah']
lines = [list(row) for row in zip(a, b, c)]
lines = [['Tom', '20', 'foo'], ...]
DanielBell99
  • 896
  • 5
  • 25
  • 57