我正在开发一个邮资应用程序,该应用程序需要根据多个邮政编码范围检查整数邮政编码,并根据邮政编码匹配的范围返回不同的代码。
每个代码都有一个以上的邮政编码范围。例如,如果邮政编码在 1000-2429、2545-2575、2640-2686 范围内或等于 2890,则应返回 M 代码。
我可以这样写:
if 1000 <= postcode <= 2429 or 2545 <= postcode <= 2575 or 2640 <= postcode <= 2686 or postcode == 2890:
return 'M'
但这似乎有很多代码行,因为有 27 个可返回代码和 77 个总范围要检查。是否有使用 Python 将整数与所有这些范围匹配的更有效(最好更简洁)的方法?
编辑:有很多优秀的解决方案四处飞扬,所以我已经实施了所有我能实施的解决方案,并对它们的性能进行了基准测试。
这个程序的环境是一个 web 服务(实际上是 Django 支持的),它需要一个一个地动态检查邮政编码区域代码。那么,我的首选实现将是一个可以快速用于每个请求的实现,并且不需要将任何进程保存在内存中,或者需要批量处理许多邮政编码。
我使用 timeit.Timer
测试了以下解决方案,每次使用随机生成的邮政编码默认重复 1000000 次。
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()
来进行可运行测试。我希望通过许多邮政编码的循环它会非常快,但如果每次都必须进行设置则不会。因此,重复 1m 次填充 numbers
, edgepairs
, --- edgeanswers
等,只有一个邮政编码范围,但实际上不是四个 fast_solver
:
1 米重复的时间:105.61 秒。
字典(哨兵)
使用预先生成的每个邮政区域代码的字典,cPickled 在源文件 (106 KB) 中,并为每次运行加载。我期待这种方法有更好的性能,但至少在我的系统上,IO 真的毁了它。该服务器是一款并非快得令人眼花缭乱的顶级 Mac Mini。
1 米重复的时间:5895.18 秒(从 10,000 次跑步中推断)。
摘要
好吧,我原以为有人会给出一个我没有考虑过的简单的“呃”答案,但事实证明这要复杂得多(甚至有点争议)。
如果在这种情况下每一纳秒的效率都算在内,我可能会保持一个单独的进程运行,该进程实现了二进制搜索或 dict 解决方案之一,并将结果保存在内存中以进行极快的查找。然而,由于 IF 树运行一百万次只需要五秒钟,这对我的小企业来说已经足够快了,这就是我最终将在我的应用程序中使用的东西。
感谢大家的贡献!
原文由 tobygriffin 发布,翻译遵循 CC BY-SA 4.0 许可协议
您可以将范围放入元组并将元组放入列表中。然后使用
any()
帮助您确定您的值是否在这些范围内。