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

Python in 操作在 tuple 和 list 中到底哪个快?

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

    依稀记得在公司刚开始写代码时,大佬看到我写这种语句

    if xxx in [xxx, xxx, ...]:
    	...
    

    告诉我,遇到这种情况,要用元组替代列表,因为 in 操作在元组中更快,类似这样

    if xxx in (xxx, xxx, ...):
    	...
    

    后来在网上查资料说是两个的数据结构的不同导致元组会比列表性能更高一点,今天闲来无事,写了一个测试,没想到元组比 list 慢的多?请各位大佬赐教 代码

    a = [i for i in range(100000000)]
    s = time.time()
    if 99999993 in a:
        print('true')
    e = time.time()
    t = e - s
    print(t) --> 1.7924060821533203
    
    c = (i for i in range(100000000))
    s = time.time()
    if 99999993 in c:
        print('true')
    e = time.time()
    t = e - s
    print(t) --> 8.226477146148682
    
    10 条回复    2020-11-19 22:08:11 +08:00
    ruanimal
        1
    ruanimal   145 天前
    你这个写法不对,第二部分并不是元祖是生成器
    Wincer
        2
    Wincer   145 天前   ❤️ 1
    首先,圆括号()的表达式不生成元组,而是生成器,你需要先把生成器转成 tuple,再使用 in 来判断:

    c = tuple(range(100000000))
    s = time.time()
    if 99999993 in c:
    print('true')
    e = time.time()
    t = e - s
    print(t)

    其次,想 in 操作更快,可以换成花括号(集合):{xxx, xxx, xxx}
    ztoben
        3
    ztoben   145 天前
    @ruanimal 忘记了这茬,感谢
    ztoben
        4
    ztoben   145 天前
    @Wincer 明白了 集合这个我知道的 谢谢
    fasionchan
        5
    fasionchan   145 天前
    其实两者一样慢,都是 O(n)操作;而且你写的是字面量,n 本来就很小,所以差别更是微乎其微。有些 tuple 字面量可以编译成常量,这样就不用每次都创建 tuple 对象,有一丁点性能优势。另外,考虑 list 和 tuple 对象的内部结构,tuple 也有些优势。具体可以参考 Python 虚拟机内部原理,特别是内建对象部分:

    https://www.imooc.com/read/76

    这个例子不如这样理解:在什么场景,就用什么数据结构。如果你需要一个不需要变化的列表,那就用 tuple 对象,而不是 list 对象。这样既避免意外修改,也获得一丁点性能优势。单从性能考虑,则有些牵强。
    fasionchan
        6
    fasionchan   145 天前
    对了,还可以观察字节码,看看不同的写法有什么差别。下面是一个可以编译成常量的 tuple 对象的例子:

    ```
    >>> dis.dis(compile('a = ("a", "b", "c")', '', 'exec'))
    1 0 LOAD_CONST 0 (('a', 'b', 'c'))
    2 STORE_NAME 0 (a)
    4 LOAD_CONST 1 (None)
    6 RETURN_VALUE
    ```

    而同样的 list 对象则不能,只能即时创建(BUILD_LIST 字节码):

    ```
    >>> dis.dis(compile('a = ["a", "b", "c"]', '', 'exec'))
    1 0 LOAD_CONST 0 ('a')
    2 LOAD_CONST 1 ('b')
    4 LOAD_CONST 2 ('c')
    6 BUILD_LIST 3
    8 STORE_NAME 0 (a)
    10 LOAD_CONST 3 (None)
    12 RETURN_VALUE
    ```

    关于 Python 代码编译过程与字节码,我上面贴的链接里面同样有介绍。
    yucongo
        7
    yucongo   145 天前
    In [112]: tuple_ = tuple(range(100000))

    In [113]: list_ = [*range(100000)]

    In [114]: %timeit 999 in list_
    65.2 µs ± 16.8 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

    In [115]: %timeit 999 in tuple_
    60.3 µs ± 5.88 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

    In [116]: %timeit 999 in tuple_
    60 µs ± 4.19 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

    In [117]: %timeit 999 in list_
    54.2 µs ± 1.38 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

    其实几乎没有区别
    dingwen07
        8
    dingwen07   145 天前 via iPhone   ❤️ 1
    当你 print 的时候,最慢的是 io
    keepwalk2020
        9
    keepwalk2020   145 天前
    如果只是判断是否包含,用 set 肯定比 tuple 和 list 都快,
    如果集合成员固定的话,用 frozenset 会更快一点,毕竟 frozenset 比 set 少调用了一次源码里的 set_clear_internal 函数。
    freakxx
        10
    freakxx   143 天前
    @keepwalk2020 #9

    老哥你源码看这么细的吗,

    想请教你是特意看过这部分,还是说有什么比较“系统”的一个思路去看到这么细节?
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3563 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 06:11 · PVG 14:11 · LAX 23:11 · JFK 02:11
    ♥ Do have faith in what you're doing.