如何用 map 和 re 来优化下面的 if..else..

2023-06-23 14:02:13 +08:00
 ladypxy

代码如下

long_string ="blablabl"

my_dict = {"a": "string1", "b": "string2" , "c": "string3", "d":"string4"}

if "Andy" in long_string:
    return mydict["a"]
elif "Jack" in long_string:
    return mydict["b"]
elif "Jim" in long_string and "Mike" not in long_string:
    return mydict["c"]
else:
    return mydict["d"]

感觉这样效率有点低,感觉用 map 处理比较好。但是条件 3 实际上是 2 个条件,这样用 map 反而不好写了。

请问大家有啥好的建议?

谢谢

3612 次点击
所在节点    Python
47 条回复
BeautifulSoap
2023-06-23 17:52:56 +08:00
@luozic cc: @luozic

1. 单纯的可读性,我觉得 lz 这一坨 if 已经是可读性很好了,没必要纠结,因为这段代码最大的问题不在这一坨 if 的可读性上。如果 if 的判断条件非常多,而且确保 if 中只有 and 的逻辑关系,应该用数组而不是字典。可以参考这里第一个代码片段。本质上就是把 if 给写成数组
https://gist.github.com/WonderfulSoap/333ccfd1b017728b928354bacdd1fa47

2. lz 代码主要问题出在每一个 if 判断就要从头查找一遍 long_string 。Andy 没找到,下一个 if 文里就又从头找一遍 Jack ,一次类推。如果要找的关键词很多最坏的情况要遍历 long_string N 次。所以 lz 代码最大问题是应该怎么优化这个问题争取只遍历一遍 long_string 就行。解决办法就是写一个工具类,如 FindWord("Andy"),然后一个个字符往里面塞,然后找到了的话返回找到
这代码参考第二个代码片段
https://gist.github.com/WonderfulSoap/333ccfd1b017728b928354bacdd1fa47
luozic
2023-06-23 18:02:07 +08:00
明明更合适的是更少的遍历, 一次就把要映射的 dict 全取出来。
@BeautifulSoap kmp or dfa 整上啊。。。
BeautifulSoap
2023-06-23 18:05:39 +08:00
@BeautifulSoap 优化了一下第二个代码,防止 Andy 存在时依旧遍历了整个数组
BeautifulSoap
2023-06-23 18:06:41 +08:00
@luozic 不好意思,俺整不来 DFA orz
Ericcccccccc
2023-06-23 18:07:19 +08:00
很显然, 原始的代码是最好的, 下面的"优化"都是啥..
jeesk
2023-06-23 18:10:13 +08:00
分支多就用 when, 一半情况下直接 if else, 除非是代码量极大或者需要定制化才用策略模式, 切勿过渡优化
qwq11
2023-06-23 18:22:32 +08:00
@luozic #20 单词错误没找到,可能是打快了,语法不是 py 的语法,伪代码而已,思路到位就行
fairyex
2023-06-23 18:24:17 +08:00
这类问题最适合问 GPT4 了
wxf666
2023-06-23 18:36:01 +08:00
用 Python 实现的逐字符遍历,可能还真的比不上几次 in 呢。。

可能用正则来干可以。/Andy|Jack|Jim|Mike/,然后匹配到后回调判断
luozic
2023-06-23 18:39:49 +08:00
隐写反写回来? pypi 上 dfa kmp 有啊 https://pypi.org/project/kmp-utils/
fregie
2023-06-23 18:48:45 +08:00
如果是想优化性能(虽然我觉得你的目的可能不是),用"Andy","Jack"这些构建一个 radix tree ,再来匹配 long_string 。
ladypxy
2023-06-23 20:53:58 +08:00
还是试着用 re 改写了下

long_string = "blablabl"

my_dict = {"a": "string1", "b": "string2" , "c": "string3", "d":"string4"}

patterns = {
r"(?=.*Andy)": "a";
r"(?=.*Jack)": "b",
r"(?=.*Jim)(?!.*Mike)": "c",
}

matches = [patterns[pattern_key] for pattern_key in patterns.keys() if re.search(pattern_key, long_string)]

if matches:
return my_dict[matches[0]]
else:
return my_dict["d"]

但是个人感觉还是不如 if ..else 直观
txhwind
2023-06-23 22:09:29 +08:00
就这种短路分支逻辑,long string 能有多 long 啊,根本不到考虑效率的时刻吧。。
zhenghuiy
2023-06-23 23:10:17 +08:00
我个人的观点是,不要拿着锤子去找钉子。。而是发现钉子后找个合适的锤子。if else 没有啥不好的,该用 if else 而故意不用反而搞复杂了。
Alias4ck
2023-06-24 00:02:25 +08:00
舍本逐末
yucongo
2023-06-24 09:32:27 +08:00
name2abc = dict(zip(names, my_dict))

_ = name2abc.get(name)
return my_dict.get(_, "string4")


如何
t133
2023-06-24 09:33:19 +08:00
这几个语句执行时间不超过 100ns 如果真的调用很多次应该考虑向量化
yucongo
2023-06-24 09:59:49 +08:00
···python
names = ["Andy", "Jack", "Jim"]
name2abc = dict(zip(names, my_dict))

for name in names:
if name in long_string:
_ = name2abc.get(name)
print(my_dict.get(_))
break
else:
print("string4")
```
这个吧,用 return 代替 print 的话,break 和 else 都可以去掉
zhouxiaoyuan
2023-06-24 10:37:55 +08:00
@ladypxy 试着帮你简化下,正则表达式可能不正确,自己修复下:
long_string = "blablabl"

my_dict = {
r"(?=.*Andy)": "string1";
r"(?=.*Jack)": "string2",
r"(?=.*Jim)(?!.*Mike)": "string3",
r".*":"string4",
}

return [my_dict[pattern_key] for pattern_key in my_dict.keys() if re.search(pattern_key, long_string)][0]
zhouxiaoyuan
2023-06-24 10:40:00 +08:00
@zhouxiaoyuan 当然,for 循环提前 return 退出性能更优,当选项变多时

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

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

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

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

© 2021 V2EX