2014-02-05 2 views
2

Python новичок здесь. Я искал довольно много для решения этого, но ничего не подходит для того, что мне нужно. Я хотел бы выделить пустой массив в начале моей программы, который имеет столбцы строк и b. Я придумал решение, но столкнулся с интересной проблемой, которую я не ожидал. Вот что я имел:Как распределить размер массива в Python

a = 7 
b = 5 
array_ab = [['?'] * b] * a 

который производит

[['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?']] 

Однако, если я пытаюсь изменить один элемент, он обрабатывает каждую строку, как и тот же объект и эффективно изменяет весь столбец на этот элемент. Так, например

array_ab[4][2] = '1' 

производит

[['?', '?', '1', '?', '?'], 
['?', '?', '1', '?', '?'], 
['?', '?', '1', '?', '?'], 
['?', '?', '1', '?', '?'], 
['?', '?', '1', '?', '?'], 
['?', '?', '1', '?', '?'], 
['?', '?', '1', '?', '?']] 

Очевидно, мне нужен лучший способ, чтобы создать пустой массив, чем умножением. Есть ли решение для этого в python? (Это было так просто в FORTRAN!)

+4

Возможно, вы захотите проверить 'numpy'. Это модуль, который много разбирается с массивами. – IanAuld

+0

Это не * лечить * каждую строку как один и тот же объект, каждая строка * есть * тот же объект; умножение только что создало много ссылок на этот объект. Обычно вам не нужно заранее выделять список в Python, они растут по мере добавления к ним элементов. Таким образом, ваш подход к основной проблеме может быть не идеальным - что именно вы планируете делать? –

+0

Тим, спасибо за разъяснение! Я пишу текстовое приключение и хотел создать небольшую «миникарту» сетку, которая начинает пустую, но заполняется, когда вы двигаетесь, меняя «?» до '1'. – JohannesKepler

ответ

5

Что-то вдоль линий

In [12]: a = 5 

In [13]: b = 7 

In [14]: array_ab = [ [ '?' for i in xrange(a) ] for j in xrange(b) ] 

In [15]: array_ab 
Out[15]: 
[['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?']] 

In [16]: array_ab[4][2] = '1' 

In [17]: array_ab 
Out[17]: 
[['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '1', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?']] 

В частности, вы используете list comprehensions и xrange.

3

Использование list comprehension[['?'] * b for _ in range(a)]:

In [1405]: a = 7 
     ...: b = 5 
     ...: array_ab = [['?'] * b for _ in range(a)] 

In [1406]: array_ab 
Out[1406]: 
[['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?']] 

In [1407]: array_ab[4][2] = '1' 

In [1408]: array_ab 
Out[1408]: 
[['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '1', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?']] 

['?']*b является безопасным, так как '?' является неизменна строки, изменением элементов списка строки не влияет на другие:

In [1419]: a=['a']*5 

In [1420]: a[2]=123 

In [1421]: a 
Out[1421]: ['a', 'a', 123, 'a', 'a'] 

в то время как [[1,2]]*3 опасно, потому что список изменяемых, это равно:

In [1427]: b=[1,2] 
     ...: a=[b,b,b] #a is just a list of b's references 
     ...: print a 
[[1, 2], [1, 2], [1, 2]] 

изменение элементы внутреннего списка b не влияют на содержимое a.

+0

Разве вы не должны получать зеркальное отражение с умножением массива? –

+0

@Shurane, no, потому что ['?'] * B делает список неизменяемых строк – zhangxaochen

3

Если вы собираетесь использовать свой массив для численных вычислений и можете жить с импортом внешней библиотеки, я бы предложил посмотреть на numpy. Он предоставляет класс массива и множество полезных операций с массивами.

Создание массива MxN просто

import numpy as np 

A = np.empty((M,N)) # Empty array 
B = np.zeros((M,N)) # Array filled with zeros 

Индексация затем делается как

x = A[i,j] 
A[4,2] = 1 

row1 = A[0, :] # or simply A[0] 
+2

+1 на 'numpy' за все, что дорого стоит вычислить. –

+1

Если вы знаете тип массива, вы можете сделать даже np.empty ((M, N), dtype = 'int') и так далее ... – ntg

2

Проблема здесь:

array_ab = [['?'] * 4] * 3 

Проблема вызвана тем, что python решает передать списки вокруг объекта refe Renče. Поскольку список является изменяемым объектом.

Но поскольку списки могут быть довольно большими, а не перемещать весь список вокруг памяти, Python решает просто использовать ссылку («указатель» в терминах C). Если вы назначаете одну другую переменную, вы присваиваете ей только ссылку. Это означает, что вы можете иметь две переменные, указывающие на тот же список в памяти:

>>> a = [1] 
>>> b = a 
>>> a[0] = 2 
>>> print b 
[2] 

Таким образом, в первой строке кода у вас есть ['?'] * 4.

Теперь ['?'] является указателем на значение ? в памяти, и когда вы умножаете его, вы получите 4 указатели на то же место в памяти.

НО когда вы изменяете один из значений, то Python знает, что указатель необходимо изменить, чтобы точки на новое значение:

>>> a = 4 * ['?'] 
>>> a 
['?', '?', '?', '?']] 

Вы можете проверить идентификатор элемента в списке:

>>> [id(v) for v in a] 
[33302480, 33302480, 33302480, 33302480] 
>>> a[0] = 1 
>>> a 
[1, '?', '?', '?'] 

Проблема возникает, когда вы умножаете этот список - вы получаете четыре копии указателя списка. Теперь, когда вы меняете одно из значений в одном списке, все четыре меняются вместе.

Предложенный подход заключается в создании списка нужной длины, а затем заполнить в каждом элементе с вновь созданным списком:

>>> A = [None] * 3 
>>> for i in range(3): 
...  A[i] = [None] * 4 
... 
>>> A 
[[None, None, None, None], [None, None, None, None], [None, None, None, None]] 
>>> 

Это создает список, содержащий 3 различных списки длины 4.

Или Вы можете использовать список понимание:

w, h = 4, 3 
A = [[None] * w for i in range(h)] 
[[None, None, None, None], [None, None, None, None], [None, None, None, None]] 

Edit 2

Основываясь на заголовке, вы не можете выделить точную память для списка в расширенном. Список Python использует какой-то алгоритм, чтобы распределить размер списка для будущего дополнительного роста.

from the source code:

/* This over-allocates proportional to the list size, making room 
* for additional growth. The over-allocation is mild, but is 
* enough to give linear-time amortized behavior over a long 
* sequence of appends() in the presence of a poorly-performing 
* system realloc(). 
* The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ... 
*/ 
+1

+1 для объяснения, а также для решения – desbo

+1

Очень информативный, Спасибо! Понимание списка определенно решает мою проблему лучше всего. – JohannesKepler

+1

+1 для упоминания 'id()', полезно посмотреть ссылки на объекты. –

0

Попробуйте это:

a = 7 
b = 5 
array_ab = [] 
for i in range(a): 
    array_ab.append([]) 
    for j in range(b): 
     array_ab[i].append('?') 

Этот код:

array_ab[4][2] = '1' 

изменит array_ab к:

[['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '1', '?', '?'], 
['?', '?', '?', '?', '?'], 
['?', '?', '?', '?', '?']] 
Смежные вопросы