Как превратить этот код в строку 1 без потери эффективности?

#python #optimization

#питон #оптимизация

Вопрос:

Чтобы улучшить читаемость, я бы хотел превратить этот код в 1-строчный.

Переменная tup_ranges представляет собой кортеж из 2 значений, например (20, 40) . Если значение elt включено в этот диапазон, то извлекается ключ, соответствующий этому значению n .

 identification = dict()
for elt in combination:
    for n, tup_range in ranges.items():
        if tup_range[0] <= elt and elt <= tup_range[1]:
            identification[elt] = n
 

Почему-то я не могу придумать хорошего текста для этого фрагмента кода…

 identification = {elt: [n for n, tup_range in ranges.items() if tup_range[0] <= elt and 
                        elt <= tup_range[1]][0] for elt in combination}
 

Работает, но медленнее из-за списка, созданного в середине… В любом случае, чтобы избавиться от этого списка, в котором все равно есть только один элемент?

Чтобы попробовать:

 ranges = {25: (20, 32), 35: (33, 45)}
combination = (30, 30, 40)
# Output:
{30: 25, 40: 35}
 

P.S: Действительно, этот вопрос имел бы место при проверке кода, но я чувствую, что это в основном неправильный дизайн понимания словаря.

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

1. to improve the readability ? Вы, вероятно, имеете в виду ухудшение

2. Вложенное понимание определенно менее читабельно, вы уверены, что оно должно быть однострочным?

3. @yatu хорошо, допустим, по личным причинам 🙂

4. FWIW: tup_range[0] <= elt <= tup_range[1]

5. @deceze Да, и не имеет значения. Тем не менее, основная проблема понимания словаря заключается в том, что я каждый раз создавал список из 1 elt. 120 нс для кода цикла for и 4,4 us для понимания dict…

Ответ №1:

Более читаемая версия вашего однострочника может быть

 identification = {elt: n for n, tup_range in ranges.items() for elt in combination if tup_range[0] <= elt <= tup_range[1] }
#{30: 25, 40: 35}
 

Или

 identification = {elt: n for n in ranges for elt in combination if ranges[n][0] <= elt <= ranges[n][1] }
 

Просто попытался рассчитать оба подхода

 ranges = {25: (20, 32), 35: (33, 45)}
combination = (30, 30, 40)
identification = {}
import time
start = time.time()
for elt in combination:
    for n, tup_range in ranges.items():
        if tup_range[0] <= elt and elt <= tup_range[1]:
            identification[elt] = n
end = time.time()
print(end-start)
#1.2159347534179688e-05
 
 ranges = {25: (20, 32), 35: (33, 45)}
combination = (30, 30, 40)
identification = {}
import time
start = time.time()
identification = {elt: n for n in ranges for elt in combination if ranges[n][0] <= elt <= ranges[n][1] }
end = time.time()
print(end-start)
#5.0067901611328125e-06
 

Понимание dict в 10 раз быстрее

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

1. Действительно, это намного лучше! Однако это все еще гораздо менее эффективно. Я добираюсь до 2.6 us (против 4.4 us для моей версии понимания dict). Код цикла for выполняется за 120 нс…

2. Насколько эффективно вы ищете?

3. Ну, тем более, что это элементарный шаг, повторяемый тысячи раз, он должен быть эффективным. Я действительно удивлен, что понимание диктатора занимает от 10 до 20 раз больше времени, чтобы завершить тот же код, написанный с помощью циклов.

4. Решаемая, эффективность та же, я неправильно использовал%timeit для измерения первого.

5. И это ненадежный способ измерить время, затраченное функцией. Теперь у обоих одинаковая скорость, спасибо 🙂