Преобразовать словарь списков в CSV с двумя столбцами

У меня есть словарь списков следующим образом:

{'banana': [1,2],
 'monkey': [5],
 'cow': [1,5,0],
 ...}

Я хочу написать CSV, который содержит одно число и слово следующим образом:

1 | banana
2 | banana
5 | monkey
1 | cow
5 | cow
0 | cow
...

с | в качестве разделителя.

Я попытался преобразовать его в список кортежей и записать его следующим образом:

for k, v in dic.items():
    for ID in v: 
        rv.append((ID, k))

with open(index_filename,'wb') as out:
    csv_out=csv.writer(out, delimiter='|')
    csv_out.writerow(['identifier','descriptor'])
    for row in rv:
        csv_out.writerow(row)

но запустил эту ошибку:

a bytes-like object is required, not 'str'

Есть ли более эффективный способ сделать это, чем преобразование в кортеж, и если нет, что не так с моим кодом?

Благодарю.

2 ответа

Вы открываете файл в двоичном / байтовом режиме, который указывается с помощью "b" в "wb". Это то, что многие люди делали в дни python2, когда "str" и "bytes" были одним и тем же, поэтому многие старые книги до сих пор учат этому.

Если вы открываете файл в режиме байтов, вы должны записывать в него байты, а не строки. str можно преобразовать в байты с помощью str.encode() метод:

f.write(some_str_variable.encode()

Однако, скорее всего, вам не нужно открывать файл в байтовом режиме.

with open(index_filename, 'w') as out:
    ...

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

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

Кортежи, подобные dicts в Python, очень эффективны, потому что они используются повсеместно. Большинство вызовов функций в Python включают создание кортежа (для позиционных аргументов) под капотом.

Что касается вашего конкретного примера, вы можете использовать выражение генератора, чтобы избежать временного списка:

entries = ((k, v) for k, l in dic.items() for v in l)

У вас все еще есть промежуточные кортежи, но они вычисляются на лету, пока вы перебираете элементы словаря. Это решение будет более эффективным в использовании памяти, чем явный список, особенно если у вас много записей.

Вы также можете просто поместить вложенный цикл непосредственно в тело with:

with open(index_filename,'wb') as out:
    csv_out=csv.writer(out, delimiter='|')
    csv_out.writerow(['identifier','descriptor'])
    for k, v in dic.items():
        for ID in v: 
            csv_out.writerow((k, ID))

Чтобы избежать повторных вызовов функций writerowВы также можете прибегнуть к writerows, который может быть быстрее.

with open(index_filename,'wb') as out:
    csv_out=csv.writer(out, delimiter='|')
    csv_out.writerow(['identifier','descriptor'])
    csv_out.writerows((k, v) for k, l in dic.items() for v in l)

Если вас действительно интересует, какой метод является самым быстрым, вы можете использовать модуль timeit Python для проведения измерений.

Другие вопросы по тегам