Python局部变量初始化
我对python相当陌生,我想知道局部变量是如何工作的。让我们从一个简单方法的示例开始:Python局部变量初始化,python,Python,我对python相当陌生,我想知道局部变量是如何工作的。让我们从一个简单方法的示例开始: def do_sth(): local_dict = { 'a': 1, 'b': 2, 'c': 3, ... } ... 让我们假设local_dict像一种常量变量一样使用。问题是:每次调用dou sth()时都会创建它,或者创建一次并保存在dou sth()内部的某个地方?每次调用dou sth()。这一点很重要,因为它允许您在函数内部修改local\u dict,并且不会对其他调
def do_sth():
local_dict = { 'a': 1, 'b': 2, 'c': 3, ... }
...
让我们假设local_dict像一种常量变量一样使用。问题是:每次调用dou sth()时都会创建它,或者创建一次并保存在dou sth()内部的某个地方?每次调用dou sth()
。这一点很重要,因为它允许您在函数内部修改local\u dict
,并且不会对其他调用产生任何影响。解释器不够聪明,无法判断您是否会更改它,特别是因为Python是如此动态,有一些非常迂回的方法可以使它更改
以下是您如何向自己证明词典不断被重新创建的方法:
def print_3():
print(3)
def do_sth():
local_dict = {'a': print_3()}
do_sth()
do_sth()
do_sth()
这张照片是3。。。三次
我认为全局变量可以优化这一点,但如果你真的想,那么这个怎么样:
def do_sth():
return do_sth.local_dict
do_sth.local_dict = {'a': print_3()}
从技术上讲,每个人都可以访问它,但更清楚地知道它属于什么。局部变量总是在函数的范围内创建的。一旦程序计数器退出函数(局部变量的作用域),垃圾收集器就会收集这些数据
您可以使用
dis
模块查看解释器的功能:
def do_sth():
d = {'a':2, 'b':3}
print(id(d))
import dis
dis.dis(do_sth)
将打印
2 0 BUILD_MAP 2
3 LOAD_CONST 1 (2)
6 LOAD_CONST 2 ('a')
9 STORE_MAP
10 LOAD_CONST 3 (3)
13 LOAD_CONST 4 ('b')
16 STORE_MAP
17 STORE_FAST 0 (d)
3 20 LOAD_GLOBAL 0 (id)
23 LOAD_FAST 0 (d)
26 CALL_FUNCTION 1
29 PRINT_ITEM
30 PRINT_NEWLINE
31 LOAD_CONST 0 (None)
34 RETURN_VALUE
这表明解释器在每次调用函数时都在重建值。其他答案都是正确的,我只想添加这个 在这种情况下,我宁愿:
constant_dict = { 'a': 1, 'b': 2, 'c': 3, ... }
def do_sth():
# do something reading constant_dict
do_sth()
或但不太可取:
def do_sth(local_dict = { 'a': 1, 'b': 2, 'c': 3, ... }):
# do something reading local_dict
do_sth()
在这两种情况下,python都不必为变量重新分配内存,因为解释器在读取python文件时声明并分配了该变量
如果我错了,请更正。很容易与
is
操作员进行检查:
def test():
return {'a': 1, 'b': 2, 'c': 3}
>>> test() is test() #did both produce the same object in memory space?
False
这对于可变对象很有意义,否则陷阱会到处出现
但是,有些值存储为常量,这些值可以使用dis
查看,因为常量是用字节码加载的:
>>> dis.dis(lambda:1)
1 0 LOAD_CONST 1 (1)
3 RETURN_VALUE
>>> dis.dis(lambda:(1,True, 10*1000, "a"))
1 0 LOAD_CONST 7 ((1, True, 10000, 'a'))
3 RETURN_VALUE
>>> dis.dis(lambda:[1,2,3]) #list is mutable
1 0 LOAD_CONST 1 (1)
3 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
9 BUILD_LIST 3
12 RETURN_VALUE
>>> dis.dis(lambda:{"a":1}) #dict is also mutable
1 0 LOAD_CONST 1 ('a')
3 LOAD_CONST 2 (1)
6 BUILD_MAP 1
9 RETURN_VALUE
我真的希望你计划扩大这个答案。@TadhgMcDonald Jensen我刚做了,但真的没什么好说的。这是一个非常简单的问题。你们想得太多了。@thebjorn的确,这只是一个小小的乐趣。我不敢相信每个人都投入了这么多心思。@m3diumendi4n请把它放在你拥有的地方(这不是你在Python中应该担心的那种优化)。一般来说,在大多数语言中,没有更改的东西都会被优化为只初始化一次,除非你指示编译器执行其他操作在Python中,解释器通常不会优化任何东西;-)会给出一些答案您错了;-)不要使用dicts作为默认参数(至少不要没有注释)。Python在dicts方面也非常快,在使用全局变量方面也相当慢,所以我不确定您是否会节省时间……我知道使用dicts或list作为默认参数可能会很危险,实际上,dousmth()必须始终在没有参数的情况下调用;-)这并不是说它很危险,但是对于任何在其他地方使用默认参数的人来说,语义通常都是令人惊讶的。如果评论中没有说“…用于记忆…”之类的话,大多数Python程序员都会担心。是的,我也会担心,但我在一些著名的第三方模块源代码中多次看到这一点。特别是在递归或迭代编号等过程中,对树级进行记忆。在使用它进行记忆时,这是正确的做法——但要注意多个线程,并再次使用注释,让下一个人知道你理解了自己在做什么。
>>> dis.dis(lambda:1)
1 0 LOAD_CONST 1 (1)
3 RETURN_VALUE
>>> dis.dis(lambda:(1,True, 10*1000, "a"))
1 0 LOAD_CONST 7 ((1, True, 10000, 'a'))
3 RETURN_VALUE
>>> dis.dis(lambda:[1,2,3]) #list is mutable
1 0 LOAD_CONST 1 (1)
3 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
9 BUILD_LIST 3
12 RETURN_VALUE
>>> dis.dis(lambda:{"a":1}) #dict is also mutable
1 0 LOAD_CONST 1 ('a')
3 LOAD_CONST 2 (1)
6 BUILD_MAP 1
9 RETURN_VALUE