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
ladypxy
V2EX  ›  Python

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

  •  1
     
  •   ladypxy · 356 天前 · 3365 次点击
    这是一个创建于 356 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码如下

    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  
       356 天前 via Android
    写个循环遍历 long_string ,一次循环就能判定了
    ladypxy
        2
    ladypxy  
    OP
       356 天前
    @BeautifulSoap 一次?求范例,谢谢
    westoy
        3
    westoy  
       356 天前
    就就几个条件, 这样写挺好的
    ccde8259
        4
    ccde8259  
       356 天前 via iPhone
    为什么是 map 和 re 而不是 DFA 啊……
    luozic
        5
    luozic  
       356 天前
    原始需求是啥? 你这算字符串转义?
    ladypxy
        6
    ladypxy  
    OP
       356 天前
    @luozic 需求就是查询字符串 long_string 里有没有指定的字符串,然后返回字典的数值。
    要求用 map 和 re 解决
    LoNeFong
        7
    LoNeFong  
       356 天前
    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  
       356 天前 via Android
    让 gpt 优化就行 专业对口
    BeautifulSoap
        9
    BeautifulSoap  
       356 天前
    @luozic 看了 lz 的回复有一点不太明白了,你想改善的到底是那一坨 if 的判断逻辑,还是想改善那一坨 if 里的多个 in 查找?
    JasonLaw
        10
    JasonLaw  
       356 天前
    @LoNeFong #7 这代码没有一点可读性🤕
    ladypxy
        11
    ladypxy  
    OP
       356 天前 via iPhone
    @BeautifulSoap 就是不要用 if 而是用 map 来实现
    blankmiss
        12
    blankmiss  
       356 天前
    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  
       356 天前 via Android
    @blankmiss 优雅,俺们 v2 就是追求这样的代码
    lhbc
        14
    lhbc  
       356 天前 via Android
    先遍历找出所有的关键词是否存在,存到一个字典里,然后判断返回

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

    除非你的字符串极长
    ladypxy
        15
    ladypxy  
    OP
       356 天前 via iPhone
    @tairan2006 试了下,需要多次才能给出差不多的答案,但是个人感觉这种可读性还不如 if else

    import re

    long_string = "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  
       356 天前 via Android
    不可读吗?比正则可读吧
    liantian
        17
    liantian  
       356 天前 via iPhone
    看了楼上各种解决方案…

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

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

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


    如何
    t133
        37
    t133  
       355 天前 via iPhone
    这几个语句执行时间不超过 100ns 如果真的调用很多次应该考虑向量化
    yucongo
        38
    yucongo  
       355 天前
    ···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  
       355 天前 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  
       355 天前 via Android
    @zhouxiaoyuan 当然,for 循环提前 return 退出性能更优,当选项变多时
    zhouxiaoyuan
        41
    zhouxiaoyuan  
       355 天前 via Android
    @zhouxiaoyuan 对了,应该用 OrderedDict([('foo', 1), ('bar', 2), ('spam', 3), ('grok', 4)])
    cornetCat
        42
    cornetCat  
       355 天前
    就 if else 挺好的,可读性好,打断点调试方便
    yucongo
        43
    yucongo  
       355 天前
    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  
       355 天前 via Android
    match
    fgwmlhdkkkw
        45
    fgwmlhdkkkw  
       355 天前 via Android
    yucongo
        46
    yucongo  
       355 天前
    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  
       335 天前 via Android
    真的如果单纯为了炫技,那可以,如果生产用,前往不要,if else 可读性最高,应届生来了都能接手,你搞的花里胡哨,性能提升了多少?如果不是,为什么搞?单纯为了装逼吗?减少代码行数?你底层最终的字节码,还不是 if else
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2901 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 13:50 · PVG 21:50 · LAX 06:50 · JFK 09:50
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.