在 Python 中自我分配安全吗?
Is self assigning safe in Python?
这几天一直很困惑这个问题:在Python中将一个变量赋值给自己会发生什么?例如,
a = a if (a>b) else b
我对引用计数机制知之甚少,对这个问题一无所知。知道答案的帮帮我,谢谢!
绝对安全。 Python 将在 =
符号之后对所有内容进行排序,以便在分配变量之前计算表达式。 我认为没有任何编程语言不安全(请参阅下面@hadik 的评论)。
what happens to the reference counts of that variable when self assigning?
简而言之,它会做正确的事。
在更长的答案中,要回答这个问题,最好通过 运行 这段代码查看反汇编:
import dis
def f(a, b):
a = a if (a>b) else b
dis.dis(f)
带注释的反汇编:
# the objects pointed by `a` and `b` starts with a positive refcount as they're part of the function argument
# increment refcount for the object pointed by `a` and `b`
0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
# COMPARE_OP (usually) returns boolean True/False
# increment the refcount of the boolean value
# and decrement the refcount of the object pointed by `a` and `b`
4 COMPARE_OP 4 (>)
# decrements the refcount of that boolean value
6 POP_JUMP_IF_FALSE 12
# increment refcount for the object pointed by `a`
8 LOAD_FAST 0 (a)
10 JUMP_FORWARD 2 (to 14)
# increment refcount for the object pointed by `b`
>> 12 LOAD_FAST 1 (b)
# decrement refcount for the object originally pointed by `a`
>> 14 STORE_FAST 0 (a)
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
一些背景信息:Python VM 是堆栈计算机。 LOAD_* 操作将一个值压入堆栈(并增加它的引用计数,因为堆栈现在有对该对象的引用),大多数其他操作将从堆栈中弹出值并将计算结果压入堆栈(减少消耗的值并递增结果)。 STORE_* 操作将对象从栈顶移动到变量(并递减变量引用的原始对象的引用计数;不需要更改被移动对象的引用计数,因为弹出堆栈并设置有效地更改为变量不会更改引用计数)。
简而言之,Python refcount 即使在多线程时也总是做正确的事情(这要归功于 GIL)。你真的永远不需要担心引用计数,如果一个对象可以从一个范围内到达,它就不会被垃圾收集。
这几天一直很困惑这个问题:在Python中将一个变量赋值给自己会发生什么?例如,
a = a if (a>b) else b
我对引用计数机制知之甚少,对这个问题一无所知。知道答案的帮帮我,谢谢!
绝对安全。 Python 将在 =
符号之后对所有内容进行排序,以便在分配变量之前计算表达式。 我认为没有任何编程语言不安全(请参阅下面@hadik 的评论)。
what happens to the reference counts of that variable when self assigning?
简而言之,它会做正确的事。
在更长的答案中,要回答这个问题,最好通过 运行 这段代码查看反汇编:
import dis
def f(a, b):
a = a if (a>b) else b
dis.dis(f)
带注释的反汇编:
# the objects pointed by `a` and `b` starts with a positive refcount as they're part of the function argument
# increment refcount for the object pointed by `a` and `b`
0 LOAD_FAST 0 (a)
2 LOAD_FAST 1 (b)
# COMPARE_OP (usually) returns boolean True/False
# increment the refcount of the boolean value
# and decrement the refcount of the object pointed by `a` and `b`
4 COMPARE_OP 4 (>)
# decrements the refcount of that boolean value
6 POP_JUMP_IF_FALSE 12
# increment refcount for the object pointed by `a`
8 LOAD_FAST 0 (a)
10 JUMP_FORWARD 2 (to 14)
# increment refcount for the object pointed by `b`
>> 12 LOAD_FAST 1 (b)
# decrement refcount for the object originally pointed by `a`
>> 14 STORE_FAST 0 (a)
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
一些背景信息:Python VM 是堆栈计算机。 LOAD_* 操作将一个值压入堆栈(并增加它的引用计数,因为堆栈现在有对该对象的引用),大多数其他操作将从堆栈中弹出值并将计算结果压入堆栈(减少消耗的值并递增结果)。 STORE_* 操作将对象从栈顶移动到变量(并递减变量引用的原始对象的引用计数;不需要更改被移动对象的引用计数,因为弹出堆栈并设置有效地更改为变量不会更改引用计数)。
简而言之,Python refcount 即使在多线程时也总是做正确的事情(这要归功于 GIL)。你真的永远不需要担心引用计数,如果一个对象可以从一个范围内到达,它就不会被垃圾收集。