2016-11-06 3 views
1

Увидел вопрос на другом сайте о куске кода Python, который управлял кем-то орехами. Это был довольно маленький, прямолинейный фрагмент кода, поэтому я посмотрел на него, понял, что он пытался сделать, затем запустил его в своей локальной системе и обнаружил, почему он запускал оригинальные гайки-опросники. Надеясь, что кто-то здесь может помочь мне понять, что происходит.Python list.append выходные значения отличаются от list.extend

Код кажется простым: попросите пользователя три значения (x, y, z) и сумму (n), итерацию всех значений, чтобы найти кортежи, сумма которых равна n, и добавьте эти кортежи в список «. решение. Но то, что он выводит, вместо всех кортежей, суммирующих n, является списком кортежей, отсчет которого равен счету кортежей, которые суммируются с n, но содержание которых все «[x, y, z] ». Пытаясь обернуть голову вокруг этого, я изменил вызов append на вызов расширения (зная, что это приведет к отключению добавленных кортежей), чтобы увидеть, изменилось ли поведение вообще. Я ожидал получить тот же результат, что и «x, y, z, x, y, z ...», а не «[x, y, z], [x, y, z]» повторно, потому что поскольку я читаю и понимаю документацию Python, это разница между добавлением и расширением списков. То, что я получил вместо этого, когда я использовал расширение, было правильными значениями кортежей, которые суммировались с n, просто вырвались из формы кортежа по расширению.

Вот код проблема:

my = [] 

x = 3 
y = 5 
z = 7 
n = 11 

part = [0,0,0] 
for i in range(x+1): 
    part[0] = i 
    for j in range(y+1): 
     part[1] = j 
     for k in range(z+1): 
      part[2] = k 
      if sum(part) == n: 
       my.append(part) 
print(my) 

и выход:

[[3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7], [3, 5, 7]] 

А вот продлить выход:

[0, 4, 7, 0, 5, 6, 1, 3, 7, 1, 4, 6, 1, 5, 5, 2, 2, 7, 2, 3, 6, 2, 4, 5, 2, 5, 4, 3, 1, 7, 3, 2, 6, 3, 3, 5, 3, 4, 4, 3, 5, 3] 

И удлинять код:

my = [] 

x = 3 
y = 5 
z = 7 
n = 11 

part = [0,0,0] 
for i in range(x+1): 
    part[0] = i 
    for j in range(y+1): 
     part[1] = j 
     for k in range(z+1): 
      part[2] = k 
      if sum(part) == n: 
       my.extend(part) 
print(my) 

Любой свет, который можно было бы пролить на это, будет очень признателен. Некоторое время я копался в Google и несколько сайтов Q &, и единственное, что я нашел в отношении добавления/расширения Python, - это вещи, которые, похоже, не имеют никакого отношения к этой проблеме.

{редактировать: окружающая среда деталь}

Кроме того, побежал это как в Python 2.7.10 и Python 3.4.3 (Cygwin под Windows 10 дома) с теми же результатами.

ответ

1

extend добавляет элементы из списка параметров в объект списка, делающий вызов. Более того, сбрасывайте объекты из одного списка в другой, не опуская первый.

append с другой стороны, только прилагает; больше ничего. Поэтому добавление объекта списка в другой список с существующей ссылкой на прилагаемый список может нанести некоторый ущерб - как в этом случае. После добавления списка part ссылается на список (поскольку вы изменяете на месте), поэтому вы по существу изменяете и (повторно) добавляете один и тот же объект списка каждый раз.

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

Или просто добавив копию part списка:

my.append(part[:]) 
my.append(list(part)) 
my.append(part.copy()) # Python 3 only 

Это добавить список, который не имеет никакой другой существующей ссылки за пределами своего нового родительского списка.

+0

Thanks; Я не искал этот нюанс из документации. Благополучие восстановлено! :-) – SteveC

+0

Попытка использования part.copy привела к тому, что объект AttributeError: 'list' не имеет атрибута 'copy' "; сделал немного больше копания и придумал «my.append (list (part))», который также решает проблему, получая список указателя текущей части, а не добавляя сам указатель на часть. – SteveC

+0

Ах да, 'list.copy' работает только в Python 3 –

1

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

Рассмотрим более простой случай:

In [320]: part=[0,0,0] 
In [321]: alist=[] 
In [322]: alist.append(part) 
In [323]: alist 
Out[323]: [[0, 0, 0]] 

append на самом деле поставить указатель на part в списке.

In [324]: alist.extend(part) 
In [325]: alist 
Out[325]: [[0, 0, 0], 0, 0, 0] 

extend поместить элементы part в списке, а не сам part.

Если мы изменим элемент в part, мы можем видеть последствие этой разницы:

In [326]: part[1]=1 
In [327]: alist 
Out[327]: [[0, 1, 0], 0, 0, 0] 

part Дописывать также изменился, но расширенная часть не сделали.

Именно поэтому ваш корпус append состоит из подсписок, а подсписчики имеют окончательное значение part - потому что все они part.

extend помещает текущие значения part в список. Мало того, что они не подсписны, но они не меняются, как изменения part.

Вот вариант по этому вопросу указатель Список:

In [333]: alist = [part]*3 
In [334]: alist 
Out[334]: [[0, 1, 0], [0, 1, 0], [0, 1, 0]] 
In [335]: alist[0][0]=2 
In [336]: part 
Out[336]: [2, 1, 0] 
In [337]: alist 
Out[337]: [[2, 1, 0], [2, 1, 0], [2, 1, 0]] 

alist содержит 3 указатели на part (не 3-х экземплярах). Измените один из этих подписок, и мы изменим их все, включая part.

+0

Благодарим вас за подробное объяснение; теперь это значительно больше смысла. – SteveC

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