Python 对变量的重新声明在内部是如何工作的?
How does Python's re-declaration of variables work internally?
我是 Python 的新手,所以对某些人来说这似乎是一个微不足道的问题。但是我很好奇当你将一个新对象绑定到一个变量时,Python 是如何在内部工作的,引用绑定到相同变量名的前一个对象。请看下面的代码作为例子——我理解 python 打破了与原始对象 'hello' 的绑定,将其绑定到新对象,但这里的事件顺序是什么? python如何打破与原始对象的联系但又引用它?
greeting = 'hello'
greeting = f'y{greeting[1:len(greeting)]}'
除了解释之外,我也非常感谢一些上下文。我知道字符串是不可变的,但是其他类型(如浮点数和整数)呢?
我是否了解 python 的内部运作方式重要吗?另外,如果 Python 在内部工作,哪里是了解更多信息的好地方?
希望我的问题很清楚。
在您的第二行中,Python 计算赋值语句的右侧,它创建一个字符串,该字符串使用 greeting
的旧绑定。只有在对该表达式求值后,它才会处理赋值运算符,该运算符将该字符串绑定到名称。都是非常线性的。
浮点数和整数也是不可变的。只有列表和字典是可变的。实际上,在任何情况下都不清楚如何修改整数对象。您不能引用对象的内部。请务必记住,在这种情况下:
i = 3
j = 4
i = i + j
最后一行只是创建了一个新的 integer/float 对象并将其绑定到 i
。 None 尝试修改整数对象 3
。
我写这篇文章试图描述 Python 对象和我们使用的名称之间的区别:
https://github.com/timrprobocom/documents/blob/main/UnderstandingPythonObjects.md
通过反汇编的解释:
>>> dis.dis('''greeting = 'hello'
... greeting = f'y{greeting[1:len(greeting)]}'
... ''')
1 0 LOAD_CONST 0 ('hello')
2 STORE_NAME 0 (greeting)
2 4 LOAD_CONST 1 ('y')
6 LOAD_NAME 0 (greeting)
8 LOAD_CONST 2 (1)
10 LOAD_NAME 1 (len)
12 LOAD_NAME 0 (greeting)
14 CALL_FUNCTION 1
16 BUILD_SLICE 2
18 BINARY_SUBSCR
20 FORMAT_VALUE 0
22 BUILD_STRING 2
24 STORE_NAME 0 (greeting)
26 LOAD_CONST 3 (None)
28 RETURN_VALUE
最左边的数字表示特定行的字节码从哪里开始。第 1 行很漂亮 self-explanatory,所以我将解释第 2 行。
您可能会注意到,您的 f-string 无法通过编译;它变成了一堆原始操作码,混合了常量段的加载和格式化占位符的评估,最终导致堆栈被构成最终字符串的所有片段所覆盖。当它们都在堆栈上时,它将所有片段放在最后 BUILD_STRING 2
(表示“从堆栈中取出顶部的两个值并将它们组合成一个字符串”)。
greeting
只是一个持有绑定的名称。它实际上没有值,只是对当前绑定到的任何对象的引用。并且原始引用完全在弹出堆栈顶部并重新绑定 greeting
.
的 STORE_NAME
之前被推入堆栈(使用 LOAD_NAME
)
简而言之,它起作用的原因是 greeting
的值在被替换时不再需要;它用于制作新字符串,然后丢弃以支持新字符串。
我是 Python 的新手,所以对某些人来说这似乎是一个微不足道的问题。但是我很好奇当你将一个新对象绑定到一个变量时,Python 是如何在内部工作的,引用绑定到相同变量名的前一个对象。请看下面的代码作为例子——我理解 python 打破了与原始对象 'hello' 的绑定,将其绑定到新对象,但这里的事件顺序是什么? python如何打破与原始对象的联系但又引用它?
greeting = 'hello'
greeting = f'y{greeting[1:len(greeting)]}'
除了解释之外,我也非常感谢一些上下文。我知道字符串是不可变的,但是其他类型(如浮点数和整数)呢? 我是否了解 python 的内部运作方式重要吗?另外,如果 Python 在内部工作,哪里是了解更多信息的好地方?
希望我的问题很清楚。
在您的第二行中,Python 计算赋值语句的右侧,它创建一个字符串,该字符串使用 greeting
的旧绑定。只有在对该表达式求值后,它才会处理赋值运算符,该运算符将该字符串绑定到名称。都是非常线性的。
浮点数和整数也是不可变的。只有列表和字典是可变的。实际上,在任何情况下都不清楚如何修改整数对象。您不能引用对象的内部。请务必记住,在这种情况下:
i = 3
j = 4
i = i + j
最后一行只是创建了一个新的 integer/float 对象并将其绑定到 i
。 None 尝试修改整数对象 3
。
我写这篇文章试图描述 Python 对象和我们使用的名称之间的区别:
https://github.com/timrprobocom/documents/blob/main/UnderstandingPythonObjects.md
通过反汇编的解释:
>>> dis.dis('''greeting = 'hello'
... greeting = f'y{greeting[1:len(greeting)]}'
... ''')
1 0 LOAD_CONST 0 ('hello')
2 STORE_NAME 0 (greeting)
2 4 LOAD_CONST 1 ('y')
6 LOAD_NAME 0 (greeting)
8 LOAD_CONST 2 (1)
10 LOAD_NAME 1 (len)
12 LOAD_NAME 0 (greeting)
14 CALL_FUNCTION 1
16 BUILD_SLICE 2
18 BINARY_SUBSCR
20 FORMAT_VALUE 0
22 BUILD_STRING 2
24 STORE_NAME 0 (greeting)
26 LOAD_CONST 3 (None)
28 RETURN_VALUE
最左边的数字表示特定行的字节码从哪里开始。第 1 行很漂亮 self-explanatory,所以我将解释第 2 行。
您可能会注意到,您的 f-string 无法通过编译;它变成了一堆原始操作码,混合了常量段的加载和格式化占位符的评估,最终导致堆栈被构成最终字符串的所有片段所覆盖。当它们都在堆栈上时,它将所有片段放在最后 BUILD_STRING 2
(表示“从堆栈中取出顶部的两个值并将它们组合成一个字符串”)。
greeting
只是一个持有绑定的名称。它实际上没有值,只是对当前绑定到的任何对象的引用。并且原始引用完全在弹出堆栈顶部并重新绑定 greeting
.
STORE_NAME
之前被推入堆栈(使用 LOAD_NAME
)
简而言之,它起作用的原因是 greeting
的值在被替换时不再需要;它用于制作新字符串,然后丢弃以支持新字符串。