Я работаю над почтовым приложением, которое требуется для проверки целочисленного почтового индекса в отношении ряда диапазонов почтовых индексов и возврата другого кода на основе того, в каком диапазоне соответствует почтовый индекс.
Каждый код имеет более одного диапазона почтовых индексов. Например, код M должен быть возвращен, если почтовый индекс находится в пределах диапазонов 1000-2429, 2545-2575, 2640-2686 или равен 2890.
Я мог бы написать это как:
if 1000 <= postcode <= 2429 or 2545 <= postcode <= 2575 or 2640 <= postcode <= 2686 or postcode == 2890:
return 'M'
но это похоже на множество строк кода, учитывая, что есть 27 возвратных кодов и 77 общих диапазонов для проверки. Есть ли более эффективный (и желательно более сжатый) метод совпадения целого со всеми этими диапазонами с использованием Python?
Edit: Там много отличных решений, летающих вокруг, поэтому я реализовал все, что мог, и сравнил их выступления.
Среда для этой программы - это веб-служба (фактически работающая от Django), которая должна проверять коды почтовых индексов один за другим, на лету. Таким образом, моя предпочтительная реализация - это та, которая может быть быстро использована для каждого запроса и не нуждается в каком-либо процессе, который должен храниться в памяти, или должен обрабатывать многие почтовые индексы навалом.
Я тестировал следующие решения, используя timeit.Timer
со 100 000 повторениями по умолчанию с использованием случайно генерируемых почтовых индексов каждый раз.
IF решение (мой оригинал)
if 1000 <= postcode <= 2249 or 2555 <= postcode <= 2574 or ...:
return 'M'
if 2250 <= postcode <= 2265 or ...:
return 'N'
...
Время для 1 м повторений: 5,11 секунды.
Диапазоны в кортежах (Jeff Mercado)
Немного более изящный в моем сознании и, конечно, легче вводить и читать диапазоны. Особенно хорошо, если они меняются со временем, что возможно. Но в моей реализации это закончилось в четыре раза медленнее.
if any(lower <= postcode <= upper for (lower, upper) in [(1000, 2249), (2555, 2574), ...]):
return 'M'
if any(lower <= postcode <= upper for (lower, upper) in [(2250, 2265), ...]):
return 'N'
...
Время для 1 м повторений: 19,61 секунды.
Установить членство (gnibbler)
Как заявил автор, "это только лучше, если вы строите набор один раз, чтобы проверять многие почтовые индексы в цикле". Но я думал, что все равно проверю.
if postcode in set(chain(*(xrange(start, end+1) for start, end in ((1000, 2249), (2555, 2574), ...)))):
return 'M'
if postcode in set(chain(*(xrange(start, end+1) for start, end in ((2250, 2265), ...)))):
return 'N'
...
Время для 1 м повторений: 339,35 секунд.
Биссект (робер-король)
Возможно, это немного выше моего уровня интеллекта. Я много узнал о модуле bisect
, но просто не мог решить, какие параметры дать find_ge()
, чтобы выполнить runnable-тест. Я ожидаю, что это будет очень быстро с циклом из многих почтовых индексов, но не для того, чтобы каждый раз выполнять настройку. Таким образом, с 1 м повторения заполнения numbers
, edgepairs
, edgeanswers
и т.д. Только для одного кода почтового региона (код M с четырьмя диапазонами), но фактически не выполняется fast_solver
:
Время для 1 м повторений: 105,61 секунды.
Дикт (дозорный)
Использование одного кода для кода почтового региона, предварительно сгенерированного, cPickled в исходном файле (106 КБ) и загружаемого для каждого запуска. Я ожидал гораздо большей производительности от этого метода, но по крайней мере в моей системе IO действительно уничтожил его. Сервер - это не совсем ослепительно-быстрый верхний уровень Mac Mini.
Время для 1 м повторений: 5895,18 секунд (экстраполировано с прогона 10000).
Резюме
Хорошо, я ожидал, что кто-то просто даст простой ответ "duh", который я не рассматривал, но оказалось, что это намного сложнее (и даже немного противоречиво).
Если бы каждая наносекунда эффективности рассчитывалась в этом случае, я бы, вероятно, сохранил отдельный процесс, который реализовал один из бинарных поисковых или диктофонных решений и сохранил результат в памяти для чрезвычайно быстрого поиска. Однако, поскольку дерево IF занимает всего пять секунд, чтобы запустить миллион раз, что достаточно быстро для моего малого бизнеса, это то, что я в конечном итоге использую в своем приложении.
Спасибо всем за вклад!