Puzzled with LOAD_FAST/STORE_FAST of python

我怕爱的太早我们不能终老 提交于 2019-12-12 12:32:23

问题


When I wrote some code, I found a interesting thing:

def test():
  l = []
  for i in range(10):
    def f():pass
    print(f)
    #l.append(f)

test()

import dis
dis.dis(test)

The output is :

<function test.<locals>.f at 0x7f46c0b0d400>
<function test.<locals>.f at 0x7f46c0b0d488>
<function test.<locals>.f at 0x7f46c0b0d400>
<function test.<locals>.f at 0x7f46c0b0d488>
<function test.<locals>.f at 0x7f46c0b0d400>
<function test.<locals>.f at 0x7f46c0b0d488>
<function test.<locals>.f at 0x7f46c0b0d400>
<function test.<locals>.f at 0x7f46c0b0d488>
<function test.<locals>.f at 0x7f46c0b0d400>
<function test.<locals>.f at 0x7f46c0b0d488>
  6           0 BUILD_LIST               0
              3 STORE_FAST               0 (l)

  7           6 SETUP_LOOP              42 (to 51)
              9 LOAD_GLOBAL              0 (range)
             12 LOAD_CONST               1 (10)
             15 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             18 GET_ITER
        >>   19 FOR_ITER                28 (to 50)
             22 STORE_FAST               1 (i)

  8          25 LOAD_CONST               2 (<code object f at 0x7f46c0bd8420, file "ts.py", line 8>)
             28 LOAD_CONST               3 ('test.<locals>.f')
             31 MAKE_FUNCTION            0
             34 STORE_FAST               2 (f)

  9          37 LOAD_GLOBAL              1 (print)
             40 LOAD_FAST                2 (f)
             43 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             46 POP_TOP
             47 JUMP_ABSOLUTE           19
        >>   50 POP_BLOCK
        >>   51 LOAD_CONST               0 (None)
             54 RETURN_VALUE

when

def test():
  l = []
  for i in range(10):
    def f():pass
    print(f)
    l.append(f)

test()

import dis
dis.dis(test)

And the output is:

<function test.<locals>.f at 0x7ff88ffe0400>
<function test.<locals>.f at 0x7ff88ffe0488>
<function test.<locals>.f at 0x7ff88ffe0510>
<function test.<locals>.f at 0x7ff88ffe0598>
<function test.<locals>.f at 0x7ff88ffe0620>
<function test.<locals>.f at 0x7ff88ffe06a8>
<function test.<locals>.f at 0x7ff88ffe0730>
<function test.<locals>.f at 0x7ff88ffe07b8>
<function test.<locals>.f at 0x7ff88ffe0840>
<function test.<locals>.f at 0x7ff88ffe08c8>
  6           0 BUILD_LIST               0
              3 STORE_FAST               0 (l)

  7           6 SETUP_LOOP              55 (to 64)
              9 LOAD_GLOBAL              0 (range)
             12 LOAD_CONST               1 (10)
             15 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             18 GET_ITER
        >>   19 FOR_ITER                41 (to 63)
             22 STORE_FAST               1 (i)

  8          25 LOAD_CONST               2 (<code object f at 0x7ff8900ab420, file "ts.py", line 8>)
             28 LOAD_CONST               3 ('test.<locals>.f')
             31 MAKE_FUNCTION            0
             34 STORE_FAST               2 (f)

  9          37 LOAD_GLOBAL              1 (print)
             40 LOAD_FAST                2 (f)
             43 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             46 POP_TOP

 10          47 LOAD_FAST                0 (l)
             50 LOAD_ATTR                2 (append)
             53 LOAD_FAST                2 (f)
             56 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
             59 POP_TOP
             60 JUMP_ABSOLUTE           19
        >>   63 POP_BLOCK
        >>   64 LOAD_CONST               0 (None)
             67 RETURN_VALUE

If STORE_FAST "cached" the f, why in the first code snippet, the address of f is alternant?

And in the second snippet, it has two LOAD_FAST , and the result is normal.

Is LOAD_FAST/STORE_FAST did some unknow things?


回答1:


That's happening because in each alternate iteration the older function object after the re-declaration of the current f has no references left, so it is garbage collected and Python can re-use that memory space in next iteration. On the other hand in the second the list is referring to each function so they are never garbage collected.

This is an implementation dependent thing, CPython's garbage collection is based on reference count. On PyPy the output is different:

$ ~/pypy-2.4.0-linux64/bin# ./pypy 
Python 2.7.8 (f5dcc2477b97, Sep 18 2014, 11:33:30)
[PyPy 2.4.0 with GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>> def test():
....     for i in range(10):
....         def f(): pass
....         print f
.... 
>>>> test()
<function f at 0x00007f055c77d5b0>
<function f at 0x00007f055c77d628>
<function f at 0x00007f055c77d6a0>
<function f at 0x00007f055c77d718>
<function f at 0x00007f055c77d790>
<function f at 0x00007f055c77d808>
<function f at 0x00007f055c77d880>
<function f at 0x00007f055c77d8f8>
<function f at 0x00007f055c77d970>
<function f at 0x00007f055c77d9e8>


来源:https://stackoverflow.com/questions/28088157/puzzled-with-load-fast-store-fast-of-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!