惯用Python:文字中的`in`关键字

Dav*_*ann 2 python idiomatic

in文字上使用运算符时,对于该文字来说,最常用的是列表,集合还是元组?

例如

for x in {'foo', 'bar', 'baz'}:
    doSomething(x)

...

if val in {1, 2, 3}:
    doSomethingElse(val)
Run Code Online (Sandbox Code Playgroud)

我没有看到列表有任何好处,但是元组的不可变性意味着它可以由高效的解释器提升或重用.if如果是,如果它被重用,那么效率就会受益.

哪个是最惯用的,哪个在cpython中表现最好?

jua*_*aga 7

Python提供了一个反汇编程序,因此您通常只需检查字节码:

In [4]: def checktup():
   ...:     for _ in range(10):
   ...:         if val in (1, 2, 3):
   ...:             print("foo")
   ...:

In [5]: def checkset():
   ...:     for _ in range(10):
   ...:         if val in {1, 2, 3}:
   ...:             print("foo")
   ...:


In [6]: import dis
Run Code Online (Sandbox Code Playgroud)

对于tuple文字:

In [7]: dis.dis(checktup)
  2           0 SETUP_LOOP              32 (to 34)
              2 LOAD_GLOBAL              0 (range)
              4 LOAD_CONST               1 (10)
              6 CALL_FUNCTION            1
              8 GET_ITER
        >>   10 FOR_ITER                20 (to 32)
             12 STORE_FAST               0 (_)

  3          14 LOAD_GLOBAL              1 (val)
             16 LOAD_CONST               6 ((1, 2, 3))
             18 COMPARE_OP               6 (in)
             20 POP_JUMP_IF_FALSE       10

  4          22 LOAD_GLOBAL              2 (print)
             24 LOAD_CONST               5 ('foo')
             26 CALL_FUNCTION            1
             28 POP_TOP
             30 JUMP_ABSOLUTE           10
        >>   32 POP_BLOCK
        >>   34 LOAD_CONST               0 (None)
             36 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

对于set-literal:

In [8]: dis.dis(checkset)
  2           0 SETUP_LOOP              32 (to 34)
              2 LOAD_GLOBAL              0 (range)
              4 LOAD_CONST               1 (10)
              6 CALL_FUNCTION            1
              8 GET_ITER
        >>   10 FOR_ITER                20 (to 32)
             12 STORE_FAST               0 (_)

  3          14 LOAD_GLOBAL              1 (val)
             16 LOAD_CONST               6 (frozenset({1, 2, 3}))
             18 COMPARE_OP               6 (in)
             20 POP_JUMP_IF_FALSE       10

  4          22 LOAD_GLOBAL              2 (print)
             24 LOAD_CONST               5 ('foo')
             26 CALL_FUNCTION            1
             28 POP_TOP
             30 JUMP_ABSOLUTE           10
        >>   32 POP_BLOCK
        >>   34 LOAD_CONST               0 (None)
             36 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

您会注意到,在这两种情况下,功能都会LOAD_CONST,即两次优化.甚至更好,在的情况下set文字,编译器节省了frozenset,其功能的建设过程中,窥视孔的优化已经设法找出可以成为一个不可改变的等同set.

注意,在Python 2上,编译器每次都会构建一个集合!:

In [1]: import dis

In [2]: def checkset():
   ...:     for _ in range(10):
   ...:         if val in {1, 2, 3}:
   ...:             print("foo")
   ...:

In [3]: dis.dis(checkset)
  2           0 SETUP_LOOP              49 (to 52)
              3 LOAD_GLOBAL              0 (range)
              6 LOAD_CONST               1 (10)
              9 CALL_FUNCTION            1
             12 GET_ITER
        >>   13 FOR_ITER                35 (to 51)
             16 STORE_FAST               0 (_)

  3          19 LOAD_GLOBAL              1 (val)
             22 LOAD_CONST               2 (1)
             25 LOAD_CONST               3 (2)
             28 LOAD_CONST               4 (3)
             31 BUILD_SET                3
             34 COMPARE_OP               6 (in)
             37 POP_JUMP_IF_FALSE       13

  4          40 LOAD_CONST               5 ('foo')
             43 PRINT_ITEM
             44 PRINT_NEWLINE
             45 JUMP_ABSOLUTE           13
             48 JUMP_ABSOLUTE           13
        >>   51 POP_BLOCK
        >>   52 LOAD_CONST               0 (None)
             55 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)