如何用 map 和 re 来优化下面的 if..else.. - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Dragonish3600
V2EX    Python

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

  •  1
     
  •   Dragonish3600 2023-06-23 14:02:13 +08:00 4200 次点击
    这是一个创建于 841 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下

    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 反而不好写了。

    请问大家有啥好的建议?

    谢谢

    47 条回复    2023-07-14 17:43:13 +08:00
    BeautifulSoap
        1
    BeautifulSoap  
       2023-06-23 14:58:05 +08:00 via Android
    写个循环遍历 long_string ,一次循环就能判定了
    Dragonish3600
        2
    Dragonish3600  
    OP
       2023-06-23 15:03:25 +08:00
    @BeautifulSoap 一次?求范例,谢谢
    westoy
        3
    westoy  
       2023-06-23 15:10:10 +08:00
    就就几个条件, 这样写挺好的
    ccde8259
        4
    ccde8259  
       2023-06-23 15:16:16 +08:00 via iPhone
    为什么是 map 和 re 而不是 DFA 啊……
    luozic
        5
    luozic  
       2023-06-23 15:36:15 +08:00
    原始需求是啥? 你这算字符串转义?
    Dragonish3600
        6
    Dragonish3600  
    OP
       2023-06-23 15:51:33 +08:00
    @luozic 需求就是查询字符串 long_string 里有没有指定的字符串,然后返回字典的数值。
    要求用 map 和 re 解决
    LoNeFong
        7
    LoNeFong  
       2023-06-23 15:59:25 +08:00
    long_string = "blablabl"

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

    result = my_dict["a"] if "Andy" in long_string else my_dict["b"] if "Jack" in long_string else my_dict["c"] if "Jim" in long_string and "Mike" not in long_string else my_dict["d"]

    result
    tairan2006
        8
    tairan2006  
       2023-06-23 16:07:56 +08:00 via Android
    让 gpt 优化就行 专业对口
    BeautifulSoap
        9
    BeautifulSoap  
       2023-06-23 16:16:18 +08:00
    @luozic 看了 lz 的回复有一点不太明白了,你想改善的到底是那一坨 if 的判断逻辑,还是想改善那一坨 if 里的多个 in 查找?
    JasonLaw
        10
    JasonLaw  
       2023-06-23 16:19:57 +08:00
    @LoNeFong #7 这代码没有一点可读性
    Dragonish3600
        11
    Dragonish3600  
    OP
       2023-06-23 16:37:38 +08:00 via iPhone
    @BeautifulSoap 就是不要用 if 而是用 map 来实现
    blankmiss
        12
    blankmiss  
       2023-06-23 16:41:12 +08:00
    gpt 给的答案 def get_value(long_string):
    string_map = {
    "Andy": "string1",
    "Jack": "string2",
    "Jim": "string3",
    "Mike": "not_found",
    }

    return next(map(lambda key: string_map[key], filter(lambda key: key in long_string and (key != "Jim" or "Mike" not in long_string), string_map)), "string4")

    可是这样真的好吗
    Jakarta
        13
    Jakarta  
       2023-06-23 16:49:27 +08:00 via Android
    @blankmiss 优雅,俺们 v2 就是追求这样的代码
    lhbc
        14
    lhbc  
       2023-06-23 16:56:34 +08:00 via Android
    先遍历找出所有的关键词是否存在,存到一个字典里,然后判断返回

    但没必要这样优化
    你的逻辑是满足其中一个条件就 return 了,其他都没必要找
    先找出所有的反而效率低

    除非你的字符串极长
    Dragonish3600
        15
    Dragonish3600  
    OP
       2023-06-23 16:56:40 +08:00 via iPhone
    @tairan2006 试了下,需要多次才能给出差不多的答案,但是个人感觉这种可读性还不如 if else

    import re

    lOngstring= "blablabl"

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

    keyword_mapping = {
    "Andy": "a",
    "Jack": "b",
    "Jim": "c",
    "Mike": "d"
    }

    matched_keywords = set(filter(lambda x: re.search(x, long_string) is not None, keyword_mapping.keys()))
    matched_values = [my_dict[keyword_mapping[keyword]] for keyword in matched_keywords]

    if "Jim" in matched_keywords and "Mike" not in matched_keywords:
    result = my_dict[keyword_mapping["Jim"]]
    else:
    result = matched_values[0] if matched_values else my_dict["d"]

    print(result)
    Pastsong
        16
    Pastsong  
       2023-06-23 17:08:06 +08:00 via Android
    不可读吗?比正则可读吧
    liantian
        17
    liantian  
       2023-06-23 17:23:41 +08:00 via iPhone
    看了楼上各种解决方案…

    还是原始方案最可读啊。
    qwq11
        18
    qwq11  
       2023-06-23 17:35:24 +08:00
    luozic
        19
    luozic  
       2023-06-23 17:36:52 +08:00
    @BeautifulSoap 看这 lz 还是有点故意把能优化成 DFA 的,用 re+map 处理? 关键这种字典映射,只取一个就返回?
    luozic
        20
    luozic  
       2023-06-23 17:43:44 +08:00
    https://paste.ubuntu.com/p/pFwm9WPymf/
    @qwq11 拼写单词错误,语法错误
    BeautifulSoap
        21
    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
        22
    luozic  
       2023-06-23 18:02:07 +08:00
    明明更合适的是更少的遍历, 一次就把要映射的 dict 全取出来。
    @BeautifulSoap kmp or dfa 整上啊。。。
    BeautifulSoap
        23
    BeautifulSoap  
       2023-06-23 18:05:39 +08:00
    @BeautifulSoap 优化了一下第二个代码,防止 Andy 存在时依旧遍历了整个数组
    BeautifulSoap
        24
    BeautifulSoap  
       2023-06-23 18:06:41 +08:00
    @luozic 不好意思,俺整不来 DFA orz
    Ericcccccccc
        25
    Ericcccccccc  
       2023-06-23 18:07:19 +08:00
    很显然, 原始的代码是最好的, 下面的"优化"都是啥..
    jeesk
        26
    jeesk  
       2023-06-23 18:10:13 +08:00
    分支多就用 when, 一半情况下直接 if else, 除非是代码量极大或者需要定制化才用策略模式, 切勿过渡优化
    qwq11
        27
    qwq11  
       2023-06-23 18:22:32 +08:00
    @luozic #20 单词错误没找到,可能是打快了,语法不是 py 的语法,伪代码而已,思路到位就行
    fairyex
        28
    fairyex  
       2023-06-23 18:24:17 +08:00 via Android
    这类问题最适合问 GPT4 了
    wxf666
        29
    wxf666  
       2023-06-23 18:36:01 +08:00 via Android
    用 Python 实现的逐字符遍历,可能还真的比不上几次 in 呢。。

    可能用正则来干可以。/Andy|Jack|Jim|Mike/,然后匹配到后回调判断
    luozic
        30
    luozic  
       2023-06-23 18:39:49 +08:00
    隐写反写回来? pypi 上 dfa kmp 有啊 https://pypi.org/project/kmp-utils/
    fregie
        31
    fregie  
       2023-06-23 18:48:45 +08:00
    如果是想优化性能(虽然我觉得你的目的可能不是),用"Andy","Jack"这些构建一个 radix tree ,再来匹配 long_string 。
    Dragonish3600
        32
    Dragonish3600  
    OP
       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
        33
    txhwind  
       2023-06-23 22:09:29 +08:00
    就这种短路分支逻辑,long string 能有多 long 啊,根本不到考虑效率的时刻吧。。
    zhenghuiy
        34
    zhenghuiy  
       2023-06-23 23:10:17 +08:00
    我个人的观点是,不要拿着锤子去找钉子。。而是发现钉子后找个合适的锤子。if else 没有啥不好的,该用 if else 而故意不用反而搞复杂了。
    Alias4ck
        35
    Alias4ck  
       2023-06-24 00:02:25 +08:00   1
    舍本逐末
    yucongo
        36
    yucongo  
       2023-06-24 09:32:27 +08:00
    name2abc = dict(zip(names, my_dict))

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


    如何
    t133
        37
    t133  
       2023-06-24 09:33:19 +08:00 via iPhone
    这几个语句执行时间不超过 100ns 如果真的调用很多次应该考虑向量化
    yucongo
        38
    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
        39
    zhouxiaoyuan  
       2023-06-24 10:37:55 +08:00 via Android
    @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
        40
    zhouxiaoyuan  
       2023-06-24 10:40:00 +08:00 via Android
    @zhouxiaoyuan 当然,for 循环提前 return 退出性能更优,当选项变多时
    zhouxiaoyuan
        41
    zhouxiaoyuan  
       2023-06-24 11:00:37 +08:00 via Android
    @zhouxiaoyuan 对了,应该用 OrderedDict([('foo', 1), ('bar', 2), ('spam', 3), ('grok', 4)])
    cornetCat
        42
    cornetCat  
       2023-06-24 11:27:37 +08:00
    就 if else 挺好的,可读性好,打断点调试方便
    yucongo
        43
    yucongo  
       2023-06-24 11:34:51 +08:00
    names = ["Andy", "Jack", "Jim"]
    _ = [*map(lambda x: x in long_string, names)]
    idx = _.index(True) if True in _ else len(_)
    print(my_dict[list(my_dict)[idx]])

    坚持用 map 的话试试这个
    fgwmlhdkkkw
        44
    fgwmlhdkkkw  
       2023-06-24 13:09:10 +08:00 via Android
    match
    fgwmlhdkkkw
        45
    fgwmlhdkkkw  
       2023-06-24 13:09:54 +08:00 via Android
    yucongo
        46
    yucongo  
       2023-06-24 14:56:19 +08:00
    def f(long_string):
    names = ["Andy", "Jack", "Jim"]
    _ = [*map(lambda x: x in long_string and (True if x not in ['Jim'] else 'Mike' not in long_string), names)]
    idx = _.index(True) if True in _ else len(_)
    return my_dict[list(my_dict)[idx]]

    assert f('Andy...') == 'string1'
    assert f('AndyJack...') == 'string1'
    assert f('JackAndy...') == 'string1'
    assert f('JackAndyJim...') == 'string1'
    assert f('AndyJimMike...') == 'string1'

    assert f('Jim...') == 'string3'
    assert f('JimMike...') == 'string4'

    assert f('xyz...') == 'string4'


    ‘Jim’ 加上 'Mike' not in long_string 条件
    xzm429438709
        47
    xzm429438709  
       2023-07-14 17:43:13 +08:00 via Android
    真的如果单纯为了炫技,那可以,如果生产用,前往不要,if else 可读性最高,应届生来了都能接手,你搞的花里胡哨,性能提升了多少?如果不是,为什么搞?单纯为了装逼吗?减少代码行数?你底层最终的字节码,还不是 if else
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     979 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 31ms UTC 23:05 PVG 0:05 LAX 16:05 JFK 19:05
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86