为什么集合对象存储为冻结集而列表对象存储为元组?
Why is a set object stored as a frozenset and a list object as a tuple?
我看到了一个博客 post,其中提到了 "Use func.__code__.co_consts
to check all the constants defined in the function"。
def func():
return {1,2,3} 中的 1 个
func.__code__.co_consts
(None, 1, frozenset({1, 2, 3}))
为什么 return 是 frozenset
?
def func():
return [1,2,3] 中的 1 个
func.__code__.co_consts
(None, 1, (1,2,3))
为什么 return 是 tuple
而不是列表?从 __code__.co_consts
编辑的每个对象 return 都是 不可变的 。为什么可变常量不可变?为什么 returned 元组的第一个元素总是 None
?
这是 Python Peephole optimizer
的结果
在"Optimizations"下,它说:
BUILD_LIST + COMPARE_OP(in/not in): convert list to tuple
BUILD_SET + COMPARE_OP(in/not in): convert set to frozenset
有关详细信息,请参阅 here:
"Python uses peephole optimization of your code by either pre-calculating constant expressions or transforming certain data structures"
尤其是关于“会员测试”的部分:
"What Python for membership tests is to transform mutable data structures to its inmutable version. Lists get transformed into tuples and sets into frozensets."
co_consts
中的所有对象都是常量,即它们是不可变的。例如,您不应该能够附加到在源代码中显示为文字的列表,从而修改函数的行为。
编译器通常通过列出列表中出现的所有单个常量来表示列表文字:
>>> def f():
... a = [1, 2, 3]
... return 1 in a
...
>>> f.__code__.co_consts
(None, 1, 2, 3)
查看这个函数的字节码可以看出,函数每次执行时都会在执行时建立一个列表:
>>> dis.dis(f)
2 0 LOAD_CONST 1 (1)
2 LOAD_CONST 2 (2)
4 LOAD_CONST 3 (3)
6 BUILD_LIST 3
8 STORE_FAST 0 (a)
3 10 LOAD_CONST 1 (1)
12 LOAD_FAST 0 (a)
14 COMPARE_OP 6 (in)
16 RETURN_VALUE
一般需要创建一个新的列表,因为函数可能会修改或者return字面量定义的列表,这种情况下每次执行函数都需要对新的列表对象进行操作.
不过,在其他情况下,创建新的列表对象是一种浪费。出于这个原因,Python 的窥孔优化器可以在已知安全的某些情况下用元组替换列表,或用 frozen_set
替换集合。一种这样的情况是列表或集合文字仅用于 x [not] in <list_literal>
形式的表达式中。另一种情况是在 for
循环中使用列表文字。
窥孔优化器非常简单。它一次只看一个表情。出于这个原因,它无法检测到此优化在我上面 f
的定义中是安全的,这在功能上等同于您的示例。
我看到了一个博客 post,其中提到了 "Use func.__code__.co_consts
to check all the constants defined in the function"。
def func():
return {1,2,3} 中的 1 个
func.__code__.co_consts
(None, 1, frozenset({1, 2, 3}))
为什么 return 是 frozenset
?
def func():
return [1,2,3] 中的 1 个
func.__code__.co_consts
(None, 1, (1,2,3))
为什么 return 是 tuple
而不是列表?从 __code__.co_consts
编辑的每个对象 return 都是 不可变的 。为什么可变常量不可变?为什么 returned 元组的第一个元素总是 None
?
这是 Python Peephole optimizer
的结果在"Optimizations"下,它说:
BUILD_LIST + COMPARE_OP(in/not in): convert list to tuple
BUILD_SET + COMPARE_OP(in/not in): convert set to frozenset
有关详细信息,请参阅 here:
"Python uses peephole optimization of your code by either pre-calculating constant expressions or transforming certain data structures"
尤其是关于“会员测试”的部分:
"What Python for membership tests is to transform mutable data structures to its inmutable version. Lists get transformed into tuples and sets into frozensets."
co_consts
中的所有对象都是常量,即它们是不可变的。例如,您不应该能够附加到在源代码中显示为文字的列表,从而修改函数的行为。
编译器通常通过列出列表中出现的所有单个常量来表示列表文字:
>>> def f():
... a = [1, 2, 3]
... return 1 in a
...
>>> f.__code__.co_consts
(None, 1, 2, 3)
查看这个函数的字节码可以看出,函数每次执行时都会在执行时建立一个列表:
>>> dis.dis(f)
2 0 LOAD_CONST 1 (1)
2 LOAD_CONST 2 (2)
4 LOAD_CONST 3 (3)
6 BUILD_LIST 3
8 STORE_FAST 0 (a)
3 10 LOAD_CONST 1 (1)
12 LOAD_FAST 0 (a)
14 COMPARE_OP 6 (in)
16 RETURN_VALUE
一般需要创建一个新的列表,因为函数可能会修改或者return字面量定义的列表,这种情况下每次执行函数都需要对新的列表对象进行操作.
不过,在其他情况下,创建新的列表对象是一种浪费。出于这个原因,Python 的窥孔优化器可以在已知安全的某些情况下用元组替换列表,或用 frozen_set
替换集合。一种这样的情况是列表或集合文字仅用于 x [not] in <list_literal>
形式的表达式中。另一种情况是在 for
循环中使用列表文字。
窥孔优化器非常简单。它一次只看一个表情。出于这个原因,它无法检测到此优化在我上面 f
的定义中是安全的,这在功能上等同于您的示例。