python List comprehension and append ambiguity [duplicate]

Вы можете реализовать метод фильтра, который соответствует вашим потребностям, вот как это сделать:

function myfilter(array, test){
    var passedTest =[];
    for (var i = 0; i < array.length; i++) {
       if(test( array[i]))
          passedTest.push(array[i]);
    }

    return passedTest;
}

var passedHomes = myfilter(homes,function(currentHome){
     return ((currentHome.price <= 1000 )&& (currentHome.sqft >= 500 )&&(currentHome.num_of_beds >=2 )&&(currentHome.num_of_baths >= 2.5));
});

Надеюсь, это поможет!

440
задан Taryn 22 March 2017 в 17:14
поделиться

13 ответов

Когда вы пишете [x]*3, вы получаете, по существу, список [x, x, x]. То есть список с 3 ссылками на тот же x. Когда вы затем изменяете этот сингл x, он отображается через все три ссылки на него.

Чтобы исправить это, вам нужно убедиться, что вы создаете новый список в каждой позиции. Один из способов сделать это -

[[1]*4 for n in range(3)]

, который будет повторно оценивать [1]*4 каждый раз, а не оценивать его один раз и делать 3 ссылки на 1 список.


Вы можете удивиться почему * не может создавать независимые объекты так, как это делает понимание списка. Это потому, что оператор умножения * работает с объектами, не видя выражений. Когда вы используете * для умножения [[1] * 4] на 3, * видит только 1-элементный список [[1] * 4], а не текст выражения [[1] * 4. * не имеет понятия, как делать копии этого элемента, не знаю, как переоценить [[1] * 4], и не подозревайте, что вы даже хотите копировать, и вообще, возможно, даже не было способа скопировать элемент.

Единственный вариант * - это сделать новые ссылки на существующий подсписчик вместо того, чтобы пытаться создавать новые подсписки. Все остальное было бы непоследовательным или требовало бы значительного пересмотра основополагающих решений по языковому дизайну.

Напротив, понимание списка переоценивает выражение элемента на каждой итерации. [[1] * 4 for n in range(3)] пересчитывает [1] * 4 каждый раз по той же причине [x**2 for x in range(3)] каждый раз переоценивает x**2. Каждая оценка [1] * 4 генерирует новый список, поэтому понимание списка делает то, что вы хотели.

Кстати, [1] * 4 также не копирует элементы [1], но это не имеет значения , так как целые числа неизменны. Вы не можете сделать что-то вроде 1.value = 2 и превратить 1 в 2.

352
ответ дан user2357112 18 August 2018 в 02:24
поделиться
  • 1
    – Charles Anderson 27 October 2008 в 16:40
  • 2
    Я удивлен, что ни один орган не указывает на то, что ответ здесь вводит в заблуждение. [x]*3 хранить 3 ссылки, такие как [x, x, x], только в том случае, когда x изменен. Это не работает, например. a=[4]*3, где после a[0]=5, a=[5,4,4]. – Allanqunzi 22 May 2015 в 00:16
  • 3
    Технически это все равно правильно. [4]*3 по существу эквивалентен x = 4; [x, x, x]. Правда, это никогда не вызовет проблемы , так как 4 является неизменным. Кроме того, ваш другой пример - это не совсем другой случай. a = [x]*3; a[0] = 5 не вызовет проблем, даже если x изменен, поскольку вы не изменяете x, только изменяя a. Я бы не описал мой ответ как вводящий в заблуждение или неправильный - вы просто не можете стрелять в ногу, если вы имеете дело с неизменяемыми объектами. – CAdaker 22 May 2015 в 08:04
  • 4
    @Allanqunzi, вы ошибаетесь. Сделайте x = 1000; lst = [x]*2; lst[0] is lst[1] - & gt; True. Python не различает здесь изменяемые и неизменяемые объекты. – timgeb 17 April 2016 в 18:08
  • 5
    Технически вы можете превратить 1 в 2, но это не так просто, как 1.value = 2. – Zach Gates 12 September 2017 в 19:35
  • 6
    – Zach Gates 4 August 2018 в 16:33

Попытка объяснить это более описательно,

Операция 1:

x = [[0, 0], [0, 0]]
print(type(x)) # <class 'list'>
print(x) # [[0, 0], [0, 0]]

x[0][0] = 1
print(x) # [[1, 0], [0, 0]]

Операция 2:

y = [[0] * 2] * 2
print(type(y)) # <class 'list'>
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [1, 0]]

Заметил, почему не изменяется первый элемент первого списка не изменил второй элемент каждого списка? Это потому, что [0] * 2 действительно представляет собой список из двух чисел, и ссылка на 0 не может быть изменена.

Если вы хотите создать копии клонов, попробуйте выполнить операцию 3:

import copy
y = [0] * 2   
print(y)   # [0, 0]

y = [y, copy.deepcopy(y)]  
print(y) # [[0, 0], [0, 0]]

y[0][0] = 1
print(y) # [[1, 0], [0, 0]]

еще один интересный способ создания копий клонов, операция 4:

import copy
y = [0] * 2
print(y) # [0, 0]

y = [copy.deepcopy(y) for num in range(1,5)]
print(y) # [[0, 0], [0, 0], [0, 0], [0, 0]]

y[0][0] = 5
print(y) # [[5, 0], [0, 0], [0, 0], [0, 0]]
1
ответ дан Adil Abbasi 18 August 2018 в 02:24
поделиться

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

a
out:[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#Displaying the list

a.remove(a[0])
out:[[1, 1, 1, 1], [1, 1, 1, 1]]
# Removed the first element of the list in which you want altered number

a.append([5,1,1,1])
out:[[1, 1, 1, 1], [1, 1, 1, 1], [5, 1, 1, 1]]
# append the element in the list but the appended element as you can see is appended in last but you want that in starting

a.reverse()
out:[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
#So at last reverse the whole list to get the desired list
1
ответ дан anand tripathi 18 August 2018 в 02:24
поделиться

Я думаю, все объясняют, что происходит. Я предлагаю один из способов его решения:

myList = [[1 for i in range(4)] for j in range(3)]

myList[0][0] = 5

print myList

И у вас есть:

[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
1
ответ дан awulll 18 August 2018 в 02:24
поделиться

Давайте перепишем ваш код следующим образом:

x = 1
y = [x]
z = y * 4

myList = [z] * 3

После этого запустите следующий код, чтобы сделать все более понятным. Что делает код, в основном печатает id s полученных объектов, которые

Вернуть «идентификатор» объекта

и поможет нам идентифицировать их и проанализировать, что происходит:

print("myList:")
for i, subList in enumerate(myList):
    print("\t[{}]: {}".format(i, id(subList)))
    for j, elem in enumerate(subList):
        print("\t\t[{}]: {}".format(j, id(elem)))

И вы получите следующий результат:

x: 1
y: [1]
z: [1, 1, 1, 1]
myList:
    [0]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [1]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528
    [2]: 4300763792
        [0]: 4298171528
        [1]: 4298171528
        [2]: 4298171528
        [3]: 4298171528

Итак, теперь давайте шаг за шагом. У вас есть x, который является 1, и единственным списком элементов y, содержащим x. Ваш первый шаг - y * 4, который даст вам новый список z, который в основном [x, x, x, x], т. Е. Создает новый список, который будет содержать 4 элемента, которые являются ссылками на исходный объект x. Чистый шаг очень похож. В основном вы делаете z * 3, который является [[x, x, x, x]] * 3 и возвращает [[x, x, x, x], [x, x, x, x], [x, x, x, x]] по той же причине, что и для первого шага.

1
ответ дан bagrat 18 August 2018 в 02:24
поделиться
  • 1
    Первоначально я думал, как можно придумать эти случайные числа из вашего простого примера. Вы действительно должны упомянуть, что делает id, прежде чем вы бросаете этот код у людей. – PascalVKooten 10 June 2015 в 14:56
  • 2
    @PascalvKooten спасибо, сделано :) – bagrat 10 June 2015 в 15:06
size = 3
matrix_surprise = [[0] * size] * size
matrix = [[0]*size for i in range(size)]

Frames and Objects [/g1]

Live Python Tutor Визуализировать

88
ответ дан Boann 18 August 2018 в 02:24
поделиться
  • 1
    Я поставил ваш образ в линию. Для справок в будущем вам действительно нужно объяснить, к чему вы привязываетесь. – Blckknght 27 August 2013 в 01:42
  • 2
    Отличный инструмент! Спасибо за ссылку – Dennis 27 February 2016 в 16:43
  • 3
    Итак, почему, если мы пишем matrix = [[x] * 2], не делаем 2 элемента для одного и того же объекта, как в примере, который вы описываете, это похоже на ту же концепцию, чего я не вижу? – Ahmed Mohamed 1 July 2017 в 17:55
  • 4
    @AhmedMohamed Действительно, он делает список с двумя элементами того же самого объекта, к которому относится x. Если вы сделаете глобально уникальный объект с x = object(), а затем сделаете matrix = [[x] * 2], это произойдет как истина: matrix[0][0] is matrix[0][1] – nadrimajstor 2 July 2017 в 13:13
  • 5
    @nadrimajstor, так почему изменение в матрице [0] не влияет на матрицу [1], как в примере выше с 2d-матрицей. – Ahmed Mohamed 2 July 2017 в 13:31

Собственно, подумайте об этом в другом случае. Предположим, что если ваш список таков:

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

, и если вы напишете myList[0][0] = 5, вы получите:

>>> 
[[5, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> 

Как и ожидалось. Но так как вы определяете свою переменную списка следующим образом:

[[1] * 4] * 3

Python обработает ваши коды по этому шаблону. Поэтому, если вы напишете myList[0][0] и ваш список, как указано выше, Python обработает его, как [1]*3. Вот почему все списки первых элементов изменены.

-2
ответ дан GLHF 18 August 2018 в 02:24
поделиться

myList = [[1]*4] * 3 создает один объект списка [1,1,1,1] в памяти и копирует его ссылку 3 раза. Это эквивалентно obj = [1,1,1,1]; myList = [obj]*3. Любая модификация obj будет отражена в трех местах, где obj упоминается в списке. Правильным утверждением будет:

myList = [[1]*4 for _ in range(3)]

или

myList = [[1 for __ in range(4)] for _ in range(3)]

. Важно отметить, что * оператор в основном используется для создания список литералов. Поскольку 1 является литералом, значит, obj =[1]*4 создаст [1,1,1,1], где каждый 1 будет атомарным, а не ссылкой 1, повторяемым 4 раза. Это означает, что если мы obj[2]=42, то obj станет [1,1,42,1] не [42,42,42,42], как могут предположить некоторые.

2
ответ дан jerrymouse 18 August 2018 в 02:24
поделиться
  • 1
    Речь идет не о литералах. obj[2] = 42 заменяет ссылку на индекс 2, в отличие от мутирования объекта, на который ссылается этот индекс, что является myList[2][0] = ... (myList[2] является списком, и это изменение изменяет ссылка на индекс 0 в этом списке). Конечно, целые числа не изменяются, но множество типов объектов являются . И обратите внимание, что обозначение отображения [....] также является формой литерального синтаксиса! Не путайте сложные (например, списки) и скалярные объекты (например, целые числа), с изменяемыми или неизменяемыми объектами. – Martijn Pieters♦ 25 July 2018 в 15:52
[[1] * 4] * 3

или даже:

[[1, 1, 1, 1]] * 3

Создает список, который ссылается на внутренний [1,1,1,1] 3 раза - не три копии внутреннего списка, поэтому в любое время, когда вы изменяете список (в любом позиция), вы увидите изменение три раза.

Это то же самое, что и в этом примере:

>>> inner = [1,1,1,1]
>>> outer = [inner]*3
>>> outer
[[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]]
>>> inner[0] = 5
>>> outer
[[5, 1, 1, 1], [5, 1, 1, 1], [5, 1, 1, 1]]

, где это, вероятно, немного менее удивительно.

26
ответ дан Jim Fasarakis Hilliard 18 August 2018 в 02:24
поделиться
  • 1
    Вы можете использовать & quot; is & quot; чтобы обнаружить это. ls [0] - ls [1] возвращает True. – mipadi 27 October 2008 в 16:03

Наряду с принятым ответом, который правильно объяснил проблему, в понимании вашего списка, если вы используете python-2.x, используйте xrange(), который возвращает более эффективный генератор (range() в python 3 выполняет ту же работу ) _ вместо переменной throw n:

[[1]*4 for _ in xrange(3)]      # and in python3 [[1]*4 for _ in range(3)]

Кроме того, в качестве более Pythonic способа вы можете использовать itertools.repeat() для создания объекта итератора повторяющихся элементов:

>>> a=list(repeat(1,4))
[1, 1, 1, 1]
>>> a[0]=5
>>> a
[5, 1, 1, 1]

PS Используя numpy, если вы хотите создать только массив единиц или нулей, вы можете использовать np.ones и np.zeros и / или для использования других чисел np.repeat():

In [1]: import numpy as np

In [2]: 

In [2]: np.ones(4)
Out[2]: array([ 1.,  1.,  1.,  1.])

In [3]: np.ones((4, 2))
Out[3]: 
array([[ 1.,  1.],
       [ 1.,  1.],
       [ 1.,  1.],
       [ 1.,  1.]])

In [4]: np.zeros((4, 2))
Out[4]: 
array([[ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.],
       [ 0.,  0.]])

In [5]: np.repeat([7], 10)
Out[5]: array([7, 7, 7, 7, 7, 7, 7, 7, 7, 7])
5
ответ дан Kasramvd 18 August 2018 в 02:24
поделиться

Простыми словами это происходит потому, что в python все работает по ссылке, поэтому, когда вы создаете список списков таким образом, вы в основном получаете такие проблемы.

Чтобы решить вашу проблему, вы можете сделать либо один из них: 1. Используйте документацию numpy array для numpy.empty 2. Добавьте список, когда вы попадаете в список. 3. Вы также можете использовать словарь, если хотите

4
ответ дан Neeraj Komuravalli 18 August 2018 в 02:24
поделиться

Собственно, это именно то, чего вы ожидаете. Давайте разложим, что здесь происходит:

Вы пишете

lst = [[1] * 4] * 3

Это эквивалентно:

lst1 = [1]*4
lst = [lst1]*3

Это означает, что lst - это список с 3 элемента, указывающие на lst1. Это означает, что две следующие строки эквивалентны:

lst[0][0] = 5
lst1[0] = 5

Поскольку lst[0] - это только lst1.

Чтобы получить желаемое поведение, вы можете использовать понимание списка:

lst = [ [1]*4 for n in xrange(3) ]

В этом случае выражение переоценивается для каждого n, что приводит к другому списку.

37
ответ дан PierreBdR 18 August 2018 в 02:24
поделиться
  • 1
    Это действительно понятно для новичка, подобного мне. Спасибо! – Petite Etincelle 29 April 2016 в 08:18
  • 2
    Просто небольшое дополнение к приятному ответу здесь: очевидно, что вы имеете дело с одним и тем же объектом, если вы делаете id(lst[0][0]) и id(lst[1][0]) или даже id(lst[0]) и id(lst[1]) – Sergiy Kolodyazhnyy 17 May 2017 в 07:08

Контейнеры Python содержат ссылки на другие объекты. См. Этот пример:

>>> a = []
>>> b = [a]
>>> b
[[]]
>>> a.append(1)
>>> b
[[1]]

В этом b есть список, содержащий один элемент, который является ссылкой на список a. Список a изменен.

Умножение списка на целое эквивалентно добавлению списка к себе несколько раз (см. операции общей последовательности ). Итак, продолжаем с примера:

>>> c = b + b
>>> c
[[1], [1]]
>>>
>>> a[0] = 2
>>> c
[[2], [2]]

Мы видим, что список c теперь содержит две ссылки на список a, который эквивалентен c = b * 2.

Python FAQ также содержит объяснение этого поведения: Как создать многомерный список?

1
ответ дан zwn 18 August 2018 в 02:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: