Почему list.append() в Python изменяет каждый элемент в исходном списке?

#python #arrays #json

Вопрос:

У меня есть этот фрагмент кода:

 import json

a = '''
{
    "c": {
        "d": [
            {
                "e": "f"
            }
        ]
    }
}
'''
j = json.loads(a)
l = []

for n in range(20):
    if n % 2 == 0:
        j['c']['d'][0]['e'] = n
        l.append(j)
        
print(l)
 

Вывод этого кода:

 [{'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}, {'c': {'d': [{'e': 18}]}}]
 

Результат, которого я ожидал:

 [{'c': {'d': [{'e': 0}]}}, {'c': {'d': [{'e': 2}]}}, {'c': {'d': [{'e': 4}]}}, {'c': {'d': [{'e': 6}]}}, {'c': {'d': [{'e': 8}]}}, {'c': {'d': [{'e': 10}]}}, {'c': {'d': [{'e': 12}]}}, {'c': {'d': [{'e': 14}]}}, {'c': {'d': [{'e': 16}]}}, {'c': {'d': [{'e': 18}]}}]
 

Как получить ожидаемый результат?

Комментарии:

1. Вы неоднократно изменяете один и тот же словарь внутри j и добавляете j его в свой список. Ты никуда не копируешь j .

2. Вы изменяете один и тот же словарь и повторно добавляете его в список. Каждый элемент в списке указывает на один и тот же объект python. Таким образом, конечным результатом будет список с идентичными объектами, а не то, что вы ожидали. Вам нужно сделать копию, а затем добавить копию в список, как предложено в приведенных ниже решениях. Чтобы лучше понять, как это работает, пожалуйста, прочтите это руководство: realpython.com/python-pass-by-reference

Ответ №1:

Когда вы изменяете j и впоследствии добавляете его в l , это ссылка на тот же диктант, который вы добавляете. Вот один из способов работы с копиями:

 import copy
import json

a = '''
{
    "c": {
        "d": [
            {
                "e": "f"
            }
        ]
    }
}
'''
j = json.loads(a)
l = []
for n in range(10):
    j2 = copy.deepcopy(j)
    j2['c']['d'][0]['e'] = 2 * n
    l.append(j2)
        
print(l)
 

Ответ №2:

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

 import json
import copy

a = '''
{
    "c": {
        "d": [
            {
                "e": "f"
            }
        ]
    }
}
'''
j = json.loads(a)
l = []

for n in range(20):
    if n % 2 == 0:
        j['c']['d'][0]['e'] = n
        l.append(copy.deepcopy(j))

print(l)
 

к вашему сведению

Комментарии:

1. Спасибо за ссылку, мне следует подробнее изучить этот вопрос..

2. Вы меняете j , прежде чем сделать копию, значение которой j изменяется. Все в порядке, но создание копии перед новым именем, вероятно, наименее удивительно.