append() заменят элементы в списке, а не добавляет их

Есть код на Python

name_dict = {}

def thesaurus(*name):
    for i in name:
        first_vowel = i[0:1]
        name_dict[first_vowel] = []
        name_dict[first_vowel].append(i)
    print(name_dict)

thesaurus('Юлия', "Николай", "Юрий", "Сергей", "Никита", "Савелий")

На выходе должно получиться это:

{'Ю': ['Юрий', 'Юлия'], 'Н': ['Никита', 'Николай'], 'С': ['Савелий', 'Сергей']}

А получается это:

{'Ю': ['Юрий'], 'Н': ['Никита'], 'С': ['Савелий']}

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

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

P.S. Это мой первый вопрос на данном сайте, если я что-то сделал неправильно, не ругайте сильно!


Ответы (3 шт):

Автор решения: Universall

Вот рабочий вариант:

def my_func(names: list) -> dict:
    name_dict = {}
    for name in names:
        name_dict[name[0]] = []
    for name in names:
        name_dict[name[0]].append(name)

    return name_dict


my_func(['Юлия', "Николай", "Юрий", "Сергей", "Никита", "Савелий"])

Ваша ошибка в том, что вы обнуляете список. Из за этого в него попадают только последние имена:

name_dict[first_vowel] = []
name_dict[first_vowel].append(i)
→ Ссылка
Автор решения: Сергей Шашко
name_dict = {}

def thesaurus(*name):
    for i in name:
        first_vowel = i[0]
        if name_dict.get(first_vowel) == None:
            name_dict[first_vowel] = []
        name_dict[first_vowel].append(i)
    print(name_dict)

thesaurus('Юлия', "Николай", "Юрий", "Сергей", "Никита", "Савелий")
→ Ссылка
Автор решения: Stanislav Volodarskiy

Специально для иницилизации новых ключей есть dict.setdefault:

def thesaurus(*name):
    name_dict = {}
    for i in name:
        name_dict.setdefault(i[0], []).append(i)
    print(name_dict)


thesaurus('Юлия', "Николай", "Юрий", "Сергей", "Никита", "Савелий")
{'Ю': ['Юлия', 'Юрий'], 'Н': ['Николай', 'Никита'], 'С': ['Сергей', 'Савелий']}

Другой инструмент collections.defaultdict:

import collections


def thesaurus(*name):
    name_dict = collections.defaultdict(list)
    for i in name:
        name_dict[i[0]].append(i)
    print(name_dict)


thesaurus('Юлия', "Николай", "Юрий", "Сергей", "Никита", "Савелий")
defaultdict(<class 'list'>, {'Ю': ['Юлия', 'Юрий'], 'Н': ['Николай', 'Никита'], 'С': ['Сергей', 'Савелий']})

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

def thesaurus(*name):
    name_dict = {}
    for i in name:
        key = i[0]
        if key not in name_dict:
            name_dict[key] = []
        name_dict[key].append(i)
    print(name_dict)


thesaurus('Юлия', "Николай", "Юрий", "Сергей", "Никита", "Савелий")
{'Ю': ['Юлия', 'Юрий'], 'Н': ['Николай', 'Никита'], 'С': ['Сергей', 'Савелий']}

И ещё один вариант, на крохотную долю быстрее. Я его меньше люблю из-за дублирования функциональности:

def thesaurus(*name):
    name_dict = {}
    for i in name:
        key = i[0]
        if key in name_dict:
            name_dict[key].append(i)
        else:
            name_dict[key] = [i]
    print(name_dict)


thesaurus('Юлия', "Николай", "Юрий", "Сергей", "Никита", "Савелий")
{'Ю': ['Юлия', 'Юрий'], 'Н': ['Николай', 'Никита'], 'С': ['Сергей', 'Савелий']}

Все примеры выше смешивают частную функциональность (какие ключи и значения для телефонной книги) и общую (из пар ключ/значение сделать словарь списков). Будет правильно их разделить, общая функциональнось в функции multidict. thesaurus подготавливает данные и обращается к multidict:

def multidict(items):
    md = {}
    for k, v in items:
        md.setdefault(k, []).append(v)
    return md


def thesaurus(*name):
    name_dict = multidict((i[0], i) for i in name)
    print(name_dict)


thesaurus('Юлия', "Николай", "Юрий", "Сергей", "Никита", "Савелий")
{'Ю': ['Юлия', 'Юрий'], 'Н': ['Николай', 'Никита'], 'С': ['Сергей', 'Савелий']}
→ Ссылка