Python 's builtin min/max is evil

2017-11-14 19:53:37 +08:00
 workwonder

最近频繁被 Python 内建的 min 函数坑害,该函数接受两种调用方式: min(a, b, ...)min([a, b, ...]),然后加上其内部容错一点都没有,一不小心就跑挂了,比如参数中有 None,参数为空等 “意外” 情形。鉴于最近多次受灾,我实现了其安全替代: https://gist.github.com/wonderbeyond/fdd874f37d86534f23109f153cb671a2 (自带 doctest )

5951 次点击
所在节点    Python
65 条回复
20015jjw
2017-11-14 20:04:54 +08:00
本来自带函数就是简单为主 强行 evil
congeec
2017-11-14 20:09:08 +08:00
为嘛不用 key=func 参数?
你说他们 evil,我倒觉得这是强类型的优势
CSM
2017-11-14 20:13:59 +08:00
有时候快速失败反而易于发现 bug
zsj950618
2017-11-14 20:14:48 +08:00
>>> min(filter(None, [1,2,4, None]))
1

>>> min(filter(None, [1,2,4, '']))
1

搞那么复杂干嘛
introom
2017-11-14 20:16:28 +08:00
这个问题我也经常遇到,语言设计的缺陷
0ZXYDDu796nVCFxq
2017-11-14 20:16:51 +08:00
楼主强行把 None 去掉……
如果传入个 bool, str, dict 又怎么办?
workwonder
2017-11-14 20:20:42 +08:00
@CSM 我们的业务中,`min(t.start for t in tasks)`,其中 task.start 为 None 是很正常的,所以也不需要快速失败。
还有如果 tasks 是个空列表,我完全希望其返回 None。
n2ex2
2017-11-14 20:23:20 +08:00
@workwonder 这只是你的需求,别坑其他人。
workwonder
2017-11-14 20:23:50 +08:00
@zsj950618 我还考虑了 `min()`, `min(1)`, `min([])`,所以复杂了点。
workwonder
2017-11-14 20:24:35 +08:00
@n2ex2 危害在哪儿,欢迎举例。
n2ex2
2017-11-14 20:32:59 +08:00
不符合 min/max 逻辑的使用方式就应该报错而不应该过度包装。
n2ex2
2017-11-14 20:33:57 +08:00
你说它没有容错,其实只是你使用方式不严谨而已。
est
2017-11-14 20:40:54 +08:00
这都能够坑,LZ 就是传说中重构火葬场的人吧。

还是早点放弃动态语言为好

遇到 js 你岂不是直接疯掉。

来猜一猜 [1, 2, 3].map(parseInt) 返回多少。
billgreen1
2017-11-14 20:48:17 +08:00
危害就是 Python 里 int/float 不可以与 None 比较,你强行让他们可以比较了。再说你可以用 float("nan")代表缺失值,这是标准做法吧?
dawncold
2017-11-14 20:53:43 +08:00
印象中没遇到过问题,而且用的时候一般很明确非空列表或 generator 中取最大或最小,再就是两个值比最大或最小

例如:
```python
b2c_stock_base = max(min(v.b2c_stock_base // v.quantity for v in variants), 0)

shopper_available_count = max(0, privilege_rule.max_count_per_shopper - row.bought_count)
```

你的 task.start 表示什么意思呢?
workwonder
2017-11-14 20:55:53 +08:00
@est 让您意外了,确实被坑的狠!而且我 Python 有多年经验。哈哈!

一开始没做详尽数据数据校验,但是能正确运行很久,其它子模块逻辑微调,意外受灾。但是用我所谓的 safe 版替代,确实不用到处做那么冗杂的判断了,这也从侧面反映了内建的 min/max 不太灵活。

比如像下面这种逻辑:

>>> task.start = min(task.start, start)

原地判断一下参数写起来还是很容易出错的。
workwonder
2017-11-14 20:58:59 +08:00
@dawncold task.start 表示一个 task 没有设置开始时间。
dawncold
2017-11-14 20:59:00 +08:00
@est 看起来是 parseInt 的问题
workwonder
2017-11-14 21:02:20 +08:00
> 危害就是 Python 里 int/float 不可以与 None 比较,你强行让他们可以比较了

@billgreen1 我的命名为 min/max_ignore_none, 其实想表达 None 是未知值,参与比较是没有意义的,所以直接忽略了,不管比大还是比小。
dawncold
2017-11-14 21:05:59 +08:00
@workwonder 我觉得我会这样写:min(t.start for t in tasks if t.started)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/406388

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX