【问题标题】:Python local variable initializationPython局部变量初始化
【发布时间】:2016-09-07 10:26:14
【问题描述】:

我对 python 比较陌生,我想知道局部变量是如何工作的。让我们从一个简单方法的例子开始:

def do_sth():
    local_dict = { 'a': 1, 'b': 2, 'c': 3, ... }
    ...

让我们假设 local_dict 被用作一种 const 变量。这里的问题是:它是在每次调用 do_sth() 时创建的,还是创建一次并保存在 do_sth() 内部的某个地方?

【问题讨论】:

  • 一般来说,没有改变的东西在大多数语言中都被优化为只初始化一次,除非你指示编译器做其他事情(你可以这样做)。
  • 在 Python 中,一般来说,解释器不会优化任何东西 ;-)

标签: python


【解决方案1】:

每次调用do_sth()。这很重要,因为它允许您在函数内部对local_dict 进行修改,并且不会对其他调用产生任何影响。解释器不够聪明,无法判断您是否不会更改它,尤其是因为 Python 是如此动态,以至于您可以通过一些非常迂回的方式来改变它。

您可以通过以下方式向自己证明字典不断被重新创建:

def print_3():
    print(3)

def do_sth():
    local_dict = {'a': print_3()}

do_sth()
do_sth()
do_sth()

这会打印 3 ... 3 次。

我认为全局变量可以很好地优化这个,但如果你真的想要,这个怎么样:

def do_sth():
    return do_sth.local_dict

do_sth.local_dict = {'a': print_3()}

从技术上讲,每个人仍然可以访问它,但它属于什么更清楚。

【讨论】:

  • 我真的希望你计划扩展这个答案。
  • @TadhgMcDonald-Jensen 我刚刚做了,但真的没什么可说的。这是一个非常简单的问题。
  • 你们想多了。
  • @thebjorn 确实,这只是有点有趣。我无法相信每个人都为此付出了多少思考。
  • @m3diumendi4n 把它放在你拥有它的地方(这些不是你在 Python 中应该担心的优化类型)。
【解决方案2】:

局部变量总是在函数范围内创建。一旦程序计数器退出函数(局部变量的范围),垃圾收集器就会收集。

global_dict = []

def do_sth():
    local_dict = { 'a': 1, 'b': 2, 'c': 3, ... }
    ...

def do_other_task():
   #local_dict not visible. It's outside of scope. 
   global global_dict # fetch memory address of previously declared global_dict
   global_dict = { 'a': 1, 'b': 2, 'c': 3, ... }

【讨论】:

    【解决方案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        
    

    这表明解释器在每次调用函数时都在重建值。

    【讨论】:

      【解决方案4】:

      其他答案都是正确的,我就补充一下。

      在这种情况下,我宁愿这样做:

      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 文件时声明和分配的。

      如果我错了,请纠正我。

      【讨论】:

      • 你错了 ;-) 不要使用 dicts 作为默认参数(至少不是没有注释)。 Python 在 dicts 方面也非常快,而在使用全局变量方面则相对较慢,所以我不确定你是否会节省任何时间......
      • 我知道使用字典或列表作为默认参数可能很危险,而且确实必须始终在没有参数的情况下调用 do_smth() ;-)
      • 它并不是很危险,但是对于在其他任何地方使用默认参数的人来说,语义通常会令人惊讶。如果评论中没有说“……用于记忆……”,大多数 Python 程序员都会担心。
      • 是的,我也会担心,但我在一些著名的第三方模块源代码中多次看到这一点。尤其是在递归期间记忆树级别或迭代次数之类的事情。
      • 将它用于记忆是正确的做法 - 但要小心处理多个线程,并再次使用评论,让下一个人知道您了解自己在做什么。
      【解决方案5】:

      使用is 操作符很容易检查:

      def test():
          return {'a': 1, 'b': 2, 'c': 3}
      
      >>> test() is test() #did both produce the same object in memory space?
      False
      

      这对于可变对象很有意义,否则mutable default argument 陷阱会随处出现。

      但是,有些值存储为常量,可以使用 dis 看到这些值,因为常量是使用 LOAD_CONST 字节码加载的:

      >>> 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
      

      【讨论】:

        猜你喜欢
        • 2021-03-17
        • 2012-10-03
        • 2017-05-14
        • 2016-01-14
        • 1970-01-01
        • 2018-09-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多