2010-06-09 3 views
10

Если я хочу список инициализируется 5 нулей, это очень приятно и легко:Более элегантный способ инициализировать список дублирующихся элементов в Python

[0] * 5 

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

[[0]] * 5 

не будет работать, как задумано, так как это будет 10 копий одного и того же списка. Я должен сделать:

[[0] for i in xrange(5)] 

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

[[0] for _ in "  "] 

Но если я хочу, список списков нулей становится уродливее:

[[[0] for _ in "  "] for _ in "  "] 

все это вместо того, что я хочу сделать:

[[[0]]*5]*5 

Кто-нибудь нашел элегантный способ справиться с этой «проблемой»?

ответ

9

Подумав немного об этом, я пришел к этому решению: (7 строк без импорта)

# helper 
def cl(n, func): 
    # return a lambda, that returns a list, where func(tion) is called 
    return (lambda: [func() for _ in range(n)]) 

def matrix(base, *ns): 
    # the grid lambda (at the start it returns the base-element) 
    grid = lambda: base 

    # traverse reversed, to handle the midmost values first 
    for n in reversed(ns): 
     # assign a new lambda with the last grid within (and call it) 
     grid = cl(n, grid) 

    return grid() # call the full grid (but the matrix calls you ^^) 

Тесты дают следующие результаты:

>>> from pprint import pprint as pp 
>>> 
>>> matrix(None, 2,3) 
[[None, None, None], [None, None, None]] 
>>> 
>>> matrix(None, 4,3) 
[[None, None, None], [None, None, None], [None, None, None], [None, None, None]] 
>>> 
>>> x = matrix(None, 3,5,2) 
>>> pp(x) 
[[[None, None], [None, None], [None, None], [None, None], [None, None]], 
[[None, None], [None, None], [None, None], [None, None], [None, None]], 
[[None, None], [None, None], [None, None], [None, None], [None, None]]] 
>>> x[1][3][0] = "test" 
>>> pp(x) 
[[[None, None], [None, None], [None, None], [None, None], [None, None]], 
[[None, None], [None, None], [None, None], ['test', None], [None, None]], 
[[None, None], [None, None], [None, None], [None, None], [None, None]]] 

Другое решение, имеющее то преимущество, что используется «[[[0]] * 5] * 5" -синтекс:

def uniq(base, l): 
    # function used to replace all values with the base 
    nl = [] 
    for i in l: 
     if type(i) is list: 
      nl.append(uniq(base, i)) # recursion for deep lists 
     else: 
      nl.append(base) 
    return nl 

Тест:

# first arg is the base, the 0 inside the [] is just a dummy 
# (for what None is the best choice usually) 
>>> x = uniq(0, [[[0]]*5]*5) 
>>> x[0][3][0] = 5 
>>> pp(x) 
[[[0], [0], [0], [5], [0]], 
[[0], [0], [0], [0], [0]], 
[[0], [0], [0], [0], [0]], 
[[0], [0], [0], [0], [0]], 
[[0], [0], [0], [0], [0]]] 

кстати. NumPy библиотека имеет np.zeros(s) -функции, где s это форма, как (3,4,5)

>>> s = (2,2) 
>>> np.zeros(s) 
array([[ 0., 0.], 
     [ 0., 0.]]) 

Наконец тест производительности:

# functions are already defined ... 
import timeit 
>>> # Alex Martelli's Code 
>>> t1 = timeit.Timer(lambda: multi_dimension_list(None, 3,4,5)) 
>>> # the two mentioned above 
>>> t2 = timeit.Timer(lambda: matrix(None, 3,4,5)) 
>>> t3 = timeit.Timer(lambda: uniq(None, [[[None]*5]*4]*3)) 
>>> 
>>> t1.timeit(10000) 
2.1910018920898438 
>>> t2.timeit(10000) 
0.44953203201293945 
>>> t3.timeit(10000) 
0.48807907104492188 

Я нашел, что это действительно интересно, чтобы обнаружить эту проблему. Итак, спасибо за вопрос :)

+1

oo cute 15charararar – Claudiu

+0

Claudiu, я добавил новый метод и некоторые подробности – Joschua

+0

Почему вы не принимаете один из этих ответов? – Joschua

0

Одним из решений является, чтобы иметь вспомогательную функцию:

import copy 
def r(i,n): 
    return [copy.deepcopy(i) for _ in xrange(n)] 

затем:

r([0],5) 
r(r([0],5),5) 

Но этот синтаксис некрасиво.

2

Другим расширить класс списка:

import copy 
class mlist(list): 
    def __mul__(self, n): 
    res = mlist() 
    for _ in xrange(n): 
     for l in self: 
    res.append(copy.deepcopy(l)) 
    return res 

затем:

>>> hey = mlist([mlist([0])]) 
>>> hey 
[[0]] 
>>> hey * 4 
[[0], [0], [0], [0]] 
>>> blah = hey * 4 
>>> blah[0][0] = 9 
>>> blah 
[[9], [0], [0], [0]] 

но инициализация mlist раздражает.

5

Если бы я имел частые требования к спискам списков списков ... Я бы просто упаковать его здание в небольшую функцию фабрики, такие как:

import copy 

def multi_dimension_list(baseitem, *dimensions): 
    dimensions = list(dimensions) 
    result = [baseitem] * dimensions.pop(-1) 
    for d in reversed(dimensions): 
    result = [copy.deepcopy(result) for _ in range(d)] 
    return result 

eg = multi_dimension_list(0, 3, 4, 5) 
print(eg) 
# and just to prove the parts are independent...: 
eg[1][1][1] = 23 
print(eg) 

На практике, я не» t даже беспокоить, потому что мои применения многомерных списков такого рода немногочисленны и далеки друг от друга, так что понимание встроенного списка просто отлично. Однако общая идея создания собственного модуля небольших полезных функций для простых задач, которые вам нужно выполнять часто и (по вашему мнению), не выполняется элегантно встроенными идиомами, на самом деле это единственный путь!-)

+0

Я запустил эту функцию и столкнулся с проблемой. Операция присваивания, например, [1] [1] [1], должна только изменять элемент, например, [1] [1] [1]. Но я нахожу, что он меняет значение трех элементов. Он подключен ... – chnet

+0

Право - ему нужна глубокая копия (редактирование для исправления). –

+0

Единственное, что мне нужно для многомерных списков, было, когда мне нужны матрицы. Поэтому я просто использовал numpy. –

Смежные вопросы