Python:有效地检查整数是否在\*许多\*范围内

新手上路,请多包涵

我正在开发一个邮资应用程序,该应用程序需要根据多个邮政编码范围检查整数邮政编码,并根据邮政编码匹配的范围返回不同的代码。

每个代码都有一个以上的邮政编码范围。例如,如果邮政编码在 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 许可协议

阅读 291
1 个回答

您可以将范围放入元组并将元组放入列表中。然后使用 any() 帮助您确定您的值是否在这些范围内。

 ranges = [(1000,2429), (2545,2575), (2640,2686), (2890, 2890)]
if any(lower <= postcode <= upper for (lower, upper) in ranges):
    print('M')

原文由 Jeff Mercado 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题