Pull to refresh

Comments 8

Батенька да вы же гигант мысли, да это же просто гениальное изобретение, не знаю, как я мог жить без этого, ведь {}.get('key',None) так не удобен…

Дамс дожили, написал велосипед в ~100 строках — выложил на хабр. Что дальше? Будем стандартную библиотек изобретать?
Этот класс не претендует на вселенское открытие или всепоглощающую сингулярность.

Просто мне показалось, что вот так писать — это не по-питоновски:

data = {}
data.setdefault('departments', {}).setdefault(sale.user.department.pk, {}).setdefault('users', {}).setdefault('sale.user.pk', {}).setdefault('rows', {}).setdefault(sale.pk, {}) = {...}

Хотелось вот так:

data = ElasticDict()
data.departments[sale.user.department.pk].users[sale.user.pk].rows[sale.pk] = {...}

P.S. {}.get('key',None) — не то делает, что мне нужно.
Выглядит, конечно, диковато, но

from collections import defaultdict

data = defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(dict)))))
data['departments'][sale.user.department.pk]['users']['sale.user.pk']['rows'][sale.pk] = {...}
Можно рекурсивно:

from collections import defaultdict

recdict = lambda: defaultdict(recdict)

data = recdict()
data["qwe"]["asd"] = 123
Спасибо за идеи, мне вот еще одну подкинули в issue:

from collections import defaultdict

class DotDict(defaultdict):
    def __getattr__(self, attr):
        return self.__getitem__(attr)

    def __setattr__(self, attr, val):
        return self.__setitem__(attr, val)

def ElasticDict():
    return DotDict(ElasticDict)

data = ElasticDict()
data.divisions.sales.persons[123].name = 'Alex'
print data.divisions.sales.persons[123].name

И дали ссылку на более продвинутую реализацию моего подхода: addict
>P.S. {}.get('key',None) — не то делает, что мне нужно.

в статье:
>>1. доступ к атрибутам можно было делать без квадратных скобочек
>> 2. автоматически создавались отсутствующие аттрибуты
Проверка словарей на наличие ключей или использование setdefatult(key, {}) превращает код в нечитабельную кашу.

Окей, давайте сравним. Ваше решение:

from elasticdict import ElasticDict

data = ElasticDict()

for sale in Sale.objects.filter(...).prefetch_related(...):
    data.departments[sale.user.department.pk].users[sale.user.pk].rows[sale.pk] = {
        'base_income': sale.amount, 
        'bonus': sale.calc_bonus()
    }

Решение в лоб, без каких-либо зависимостей, даже без стандартного defaultdict:

data = {'departaments': {}}

for sale in Sale.objects.filter(...).prefetch_related(...):
    departament = data['deparaments'].setdefault(sale.user.department.pk, {'users': {}})
    user = departament['users'].setdefault(sale.user.pk, {'rows': {}})
    user['rows'][sale.pk] = {
        'base_income': sale.amount, 
        'bonus': sale.calc_bonus()
    }

Вопрос автору: оно того стоило?
Согласен, ради конкретно такого примера не стоило бы.

В моей задаче строчек для вставки в словарь сильно больше (30+), вдобавок, есть внутренняя логика if/else.
Создание промежуточных переменных не добавляет читаемости.
Я не настаиваю, что мой код абсолютно более читаемый, но найдутся те, кто со мной согласятся.
Равно как найдутся те, кто скажет, что это отстой — и последних в 4 раза больше :)
Sign up to leave a comment.

Articles