V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hellogbk
V2EX  ›  程序员

初学 Python,此函数求优化

  •  
  •   hellogbk · 2016-01-01 12:48:06 +08:00 · 4682 次点击
    这是一个创建于 3250 天前的主题,其中的信息可能已经有所发展或是发生改变。

    大家新年快乐!
    我初学 Python 一个星期左右,求优化下面这个函数,此函数的作用在于生成 IP 列表:

    def parse_ip(ip):
        ips = []
        if '-' in ip:
            segments = ip.split(".")
            seg_index = 0
            for segment in segments:
                if '-' in segment:
                    prefix = ".".join(segments[:seg_index])
                    suffix = ".".join(segments[seg_index+1:])
    
                    start, end = map(int, segment.split("-"))
    
                    new_ips = [".".join(filter(lambda s: s, map(str,[prefix, ip_segment, suffix]))) for ip_segment in range(start, end +1)]
                    for new_ip in new_ips:
                        ips += parse_ip(new_ip)
    
                    break
    
                seg_index += 1
    
        else:
            ips += [ip]
    
        return ips
    
    
    if __name__ == "__main__":
        print "\n".join(parse_ip("192.168.1-2.1-10"))
    

    输出的结果是:

    192.168.1.1
    192.168.1.2
    192.168.1.3
    192.168.1.4
    192.168.1.5
    192.168.1.6
    192.168.1.7
    192.168.1.8
    192.168.1.9
    192.168.1.10
    192.168.2.1
    192.168.2.2
    192.168.2.3
    192.168.2.4
    192.168.2.5
    192.168.2.6
    192.168.2.7
    192.168.2.8
    192.168.2.9
    192.168.2.10
    

    求指点代码还可以怎么优化,谢谢!

    30 条回复    2016-01-04 15:36:29 +08:00
    vanxining
        1
    vanxining  
       2016-01-01 13:06:35 +08:00 via Android
    楼主是来炫耀的 :-)
    firebroo
        2
    firebroo  
       2016-01-01 13:11:28 +08:00
    ddd
    hellogbk
        3
    hellogbk  
    OP
       2016-01-01 13:12:38 +08:00
    @vanxining 额,不是啊。 我是想知道怎么用更少的代码来实现同样的功能。
    semut
        4
    semut  
       2016-01-01 13:13:05 +08:00   ❤️ 1
    你所说的优化是指什么?运算速度么,还是计算逻辑,这是我的代码:
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    def expand(segment):
    if '-' not in segment:
    return [segment]
    else:
    start, end = map(int, segment.split('-'))
    return map(str, range(start, end+1))

    def parse_ip(ip):
    segments = ip.split('.')
    seg_list = map(expand, segments)
    return reduce(lambda lx, ly: [x+'.'+y for x in lx for y in ly], seg_list)

    if __name__ == "__main__":
    print "\n".join(parse_ip("192.168.1-2.1-10"))
    k9982874
        5
    k9982874  
       2016-01-01 13:16:29 +08:00 via iPhone   ❤️ 2
    手机看代码自动换行太难看,所以就是没好好读。
    能给的建议就是把函数参数改成
    parse_ip(ip_header, first_start, first_end, second_start, second_end)

    函数内直接 for 循环拼接生成 ip ,不需要再去拆字符串。
    字符串操作成本永远是最贵的。
    holyzhou
        6
    holyzhou  
       2016-01-01 13:18:30 +08:00   ❤️ 1
    感觉 一用上 filter , reduce , map , lambda ,列表推倒式这些,程序可读性立马就下去了
    hellogbk
        7
    hellogbk  
    OP
       2016-01-01 13:21:51 +08:00
    @semut 谢谢,我指的是代码能不能再少一点。因为我以前学 JAVA 的,所以感觉我在写这个函数的时候还是 JAVA 的思维多一点,就想知道用 python 的想法应该怎么写。
    wowpanda
        8
    wowpanda  
       2016-01-01 13:25:39 +08:00
    正则
    am241
        9
    am241  
       2016-01-01 13:25:59 +08:00
    只求代码更少可以用正则吧
    syv2
        10
    syv2  
       2016-01-01 13:32:43 +08:00   ❤️ 2
    回复不能写格式化的代码,我就不写函数了,反正就这么个意思:

    import itertools

    ip = "192.168.1-2.1-10"
    segs = [seg for seg in ip.split('.')]
    segs = [[int(iter) for iter in seg.split('-')] for seg in segs]
    ips = itertools.product(*segs)
    join_fuc = lambda l: '.'.join([str(i) for i in l])
    ips = [join_fuc(ip) for ip in ips]
    syv2
        11
    syv2  
       2016-01-01 13:36:22 +08:00
    上面写错了。。修正一下:


    import itertools

    ip = "192.168.1-2.1-10"
    segs = [seg for seg in ip.split('.')]
    segs = [[int(iter) for iter in seg.split('-')] for seg in segs]
    segs = [range(seg[0], seg[1]+1) if len(seg)==2 else range(seg[0], seg[0]+1) for seg in segs]
    ips = itertools.product(*segs)
    join_fuc = lambda l: '.'.join([str(i) for i in l])
    ips = [join_fuc(ip) for ip in ips]
    syv2
        12
    syv2  
       2016-01-01 13:45:45 +08:00   ❤️ 1
    map 改良版,关键地方还是那个 itertools.product 计算笛卡尔积

    import itertools

    ip = "192.168.1-2.1-10"
    segs = [seg for seg in ip.split('.')]
    segs = [[int(iter) for iter in seg.split('-')] for seg in segs]
    segs = [range(seg[0], seg[1]+1) if len(seg)==2 else range(seg[0], seg[0]+1) for seg in segs]
    ips = itertools.product(*segs)
    ips = ['.'.join(map(str, ip)) for ip in ips]
    asxalex
        13
    asxalex  
       2016-01-01 14:06:21 +08:00
    ```python
    def parse_ip(ip):
    return reduce(lambda x,y: [str(i) + "." + str(j) for i in x for j in y],[[int(k)] if k.find("-") < 0 else range(*map(int, *([int(a), int(b)+1] for (a, b) in [k.split("-")]))) for k in ip.split(".")])
    ```
    firebroo
        14
    firebroo  
       2016-01-01 14:06:47 +08:00
    @asxalex ddddddddddddd
    rim99
        15
    rim99  
       2016-01-01 14:25:58 +08:00
    @asxalex 膜拜 +1
    langyu
        16
    langyu  
       2016-01-01 14:42:47 +08:00   ❤️ 2
    有个库 叫 IPy 你直接用就行了
    ringzero
        17
    ringzero  
       2016-01-01 14:44:09 +08:00   ❤️ 1
    def ip2num(ip):
    ip = [int(x) for x in ip.split('.')]
    return ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]

    def num2ip(num):
    return '%s.%s.%s.%s' % (
    (num & 0xff000000) >> 24,
    (num & 0x00ff0000) >> 16,
    (num & 0x0000ff00) >> 8,
    num & 0x000000ff
    )

    def gen_ips(start, end):
    """生成 IP 地址"""
    # if num & 0xff 过滤掉 最后一段为 0 的 IP
    return [num2ip(num) for num in range(start, end + 1) if num & 0xff]
    Mark3K
        18
    Mark3K  
       2016-01-01 15:01:07 +08:00   ❤️ 1
    from itertools import product

    def parse_ip(ip_str):
    return ['.'.join(map(lambda x: str(x), t)) for t in product(*map(lambda x: range(int(x.split('-')[0]), int(x.split('-')[1])+1) if '-' in x else [int(x)], ip_str.split('.')))]
    haroldwu
        19
    haroldwu  
       2016-01-01 15:26:08 +08:00   ❤️ 1
    如果像 clojure 一樣有 thread macro ,函數式寫法( reduce map )其實更好用 www
    quietin
        20
    quietin  
       2016-01-01 16:04:02 +08:00   ❤️ 1
    4 楼的答案把字符拼接改成'.'.join 就是很好的答案了
    congeec
        21
    congeec  
       2016-01-01 17:05:21 +08:00   ❤️ 1
    @ringzero from Python 3.2 we have int.to_bytes
    >>> int.from_bytes(socket.inet_aton('192.168.1.1'), byteorder='big')
    3232235777

    >>> _.to_bytes(length=4, byteorder='big')
    b'\xc0\xa8\x01\x01'

    >>> socket.inet_ntoa(_)
    '192.168.1.1'
    congeec
        22
    congeec  
       2016-01-01 17:12:37 +08:00   ❤️ 1
    Python 号称 battery included, 你应该用自带的 ipaddress 库,写起来方便性能还不错
    poke707
        23
    poke707  
       2016-01-01 21:19:20 +08:00   ❤️ 1
    基本就是参考 4 楼与 10 楼的优化计算逻辑(分段解析再求笛卡尔积)。
    我来再尝试把代码意图写得更明显些
    https://gist.githubusercontent.com/Ginhing/aae1096546e94921990b/raw/8f5ab916950381f2e433e752b10be8f99c72ba65/ip_segment_parser.py
    hellogbk
        24
    hellogbk  
    OP
       2016-01-01 22:11:10 +08:00
    @poke707
    数学真的很重要啊。 我得去学学数学。
    tt0411
        25
    tt0411  
       2016-01-01 22:29:06 +08:00   ❤️ 1
    看来主要技巧是 itertools.product ,我的代码

    https://gist.github.com/7c00/9216518c4e7c35b296b8
    Gothack
        26
    Gothack  
       2016-01-01 22:38:40 +08:00   ❤️ 1
    大神们都在炫技
    congeec
        27
    congeec  
       2016-01-01 22:48:45 +08:00 via iPad   ❤️ 1
    @syv2 还是你屌。 python 的 itertools, functools, collection 这三个库真是必须掌握呢
    Orzpls
        28
    Orzpls  
       2016-01-02 13:52:34 +08:00 via Android
    马克之。
    menc
        29
    menc  
       2016-01-03 03:03:30 +08:00
    别的不说

    new_ips = [".".join(filter(lambda s: s, map(str,[prefix, ip_segment, suffix]))) for ip_segment in range(start, end +1)]

    这行代码你觉得这个可读性过关么
    Braid
        30
    Braid  
       2016-01-04 15:36:29 +08:00
    @asxalex ddd
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   874 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 21:00 · PVG 05:00 · LAX 13:00 · JFK 16:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.