Как правильно составить словарь в этом сложном случае - PullRequest
0 голосов
/ 10 мая 2018

Пример - искусственный, но у меня были подобные проблемы много раз.

db_file_names = ['f1', 'f2']  # list of database files

def make_report(filename):
    # read the database and prepare some report object
    return report_object

Теперь я хочу создать словарь: db_version -> number_of_tables. Объект отчета содержит всю необходимую информацию.

Словарь может выглядеть так:

d = {
    make_report(filename).db_version: make_report(filename).num_tables
    for filename in db_file_names
}

Этот подход иногда работает, но очень неэффективен: отчет готовится дважды для каждой базы данных.

Чтобы избежать этой неэффективности, я обычно использую один из следующих подходов:

Использовать временное хранилище:

reports = [make_report(filename) for filename in db_file_names]
d = {r.db_version: r.num_tables for r in reports}

Или используйте какой-нибудь адаптер-генератор:

def gen_data():
    for filename in db_file_names:
        report = make_report(filename)
        yield report.db_version, report.num_tables

d = {dat[0]: dat[1] for dat in gen_data()}

Но обычно только после того, как я напишу какое-то неправильное понимание, продумал и осознал, что в этом случае чистое и простое понимание невозможно.

Вопрос в том, есть ли лучший способ создать требуемый словарь в таких ситуациях?

Со вчерашнего дня (когда я решил опубликовать этот вопрос) я изобрел еще один подход, который мне нравится больше, чем все остальные:

d = {
    report.db_version: report.num_tables
    for filename in db_file_names
    for report in [make_report(filename), ]
}

но даже этот выглядит не очень хорошо.

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Вот функциональный способ:

from operator import attrgetter

res = dict(map(attrgetter('db_version', 'num_tables'),
               map(make_report, db_file_names)))

К сожалению, функциональная композиция не является частью стандартной библиотеки, но сторонняя организация toolz действительно предлагает эту функцию:

from toolz import compose

foo = compose(attrgetter('db_version', 'num_tables'), make_report)
res = dict(map(foo, db_file_names))

Концептуально, вы можете думать об этих функциональных решениях, выводящих итеративный набор кортежей, который затем может быть передан непосредственно в dict.

0 голосов
/ 10 мая 2018

Вы можете использовать:

d = {
    r.db_version: r.num_tables
    for r in map(make_report, db_file_names)
}

Обратите внимание, что в Python 3 map дает итератор, таким образом, нет ненужных затрат на хранение.

...