inspect.stack() and frame

Discussion in 'Python' started by Félix-Antoine Fortin, Mar 11, 2010.

  1. Given this code :
    # Experience with frame
    import sys
    import inspect

    def foo():
    stack = inspect.stack()
    print "foo frame : " + str(hex(id(sys._getframe())))

    def foo2():
    inspect.stack()
    print "foo2 frame : " + str(hex(id(sys._getframe())))

    def bar():
    print "bar frame : " + str(hex(id(sys._getframe())))

    foo()
    foo()

    foo2()
    foo2()

    bar()
    bar()

    Output example :
    foo frame : 0x84d2c0
    foo frame : 0x844bf0
    foo2 frame : 0x898c90
    foo2 frame : 0x898c90
    bar frame : 0x898f70
    bar frame : 0x898f70

    Why are the ids (address) of the frame for each foo call not the same?
    Or why the call to "stack = inspect.stack()" change the address of the
    frame?
    Félix-Antoine Fortin, Mar 11, 2010
    #1
    1. Advertising

  2. * Félix-Antoine Fortin:
    > Given this code :
    > # Experience with frame
    > import sys
    > import inspect
    >
    > def foo():
    > stack = inspect.stack()
    > print "foo frame : " + str(hex(id(sys._getframe())))


    hex returns a string. applying str is therefore redundant.


    > def foo2():
    > inspect.stack()
    > print "foo2 frame : " + str(hex(id(sys._getframe())))
    >
    > def bar():
    > print "bar frame : " + str(hex(id(sys._getframe())))
    >
    > foo()
    > foo()
    >
    > foo2()
    > foo2()
    >
    > bar()
    > bar()
    >
    > Output example :
    > foo frame : 0x84d2c0
    > foo frame : 0x844bf0
    > foo2 frame : 0x898c90
    > foo2 frame : 0x898c90
    > bar frame : 0x898f70
    > bar frame : 0x898f70
    >
    > Why are the ids (address) of the frame for each foo call not the same?


    You're dealing with Python objects. You're not dealing with the computer's
    machine stack. Whether you get the same id for two objects whose lifetimes don't
    overlap depends on the implementation's memory and id allocation strategy.


    > Or why the call to "stack = inspect.stack()" change the address of the
    > frame?


    Does it?


    Cheers,

    - Alf
    Alf P. Steinbach, Mar 11, 2010
    #2
    1. Advertising

  3. On Mar 11, 6:22 pm, "Alf P. Steinbach" <> wrote:
    > * F lix-Antoine Fortin:
    >
    > > Given this code :
    > > # Experience with frame
    > > import sys
    > > import inspect

    >
    > > def foo():
    > >     stack = inspect.stack()
    > >     print "foo frame : " + str(hex(id(sys._getframe())))

    >
    > hex returns a string. applying str is therefore redundant.
    >


    My bad.

    >
    >
    >
    >
    > > def foo2():
    > >     inspect.stack()
    > >     print "foo2 frame : " + str(hex(id(sys._getframe())))

    >
    > > def bar():
    > >     print "bar frame : " + str(hex(id(sys._getframe())))

    >
    > > foo()
    > > foo()

    >
    > > foo2()
    > > foo2()

    >
    > > bar()
    > > bar()

    >
    > > Output example :
    > > foo frame : 0x84d2c0
    > > foo frame : 0x844bf0
    > > foo2 frame : 0x898c90
    > > foo2 frame : 0x898c90
    > > bar frame : 0x898f70
    > > bar frame : 0x898f70

    >
    > > Why are the ids (address) of the frame for each foo call not the same?

    >
    > You're dealing with Python objects. You're not dealing with the computer's
    > machine stack. Whether you get the same id for two objects whose lifetimes don't
    > overlap depends on the implementation's memory and id allocation strategy..
    >


    Okay, I thought I got that when I read the id documentation, but now I
    get it.
    So the only to compare two ids, is by making sure their lifetimes
    overlap. In
    this case, instead of keeping the ids, I have to keep a reference on
    the frame
    to make sure it is still alive when I will compare it with a second
    one.

    Thanks!

    > > Or why the call to "stack = inspect.stack()" change the address of the
    > > frame?

    >
    > Does it?
    >


    Yeah it does... I always get N different id when I run foo() N times
    in a row.
    Actually, what you said about lifetime applies here too. Here is
    another quick
    snippet :

    import sys
    import inspect

    def foo():
    stack = inspect.stack()
    return sys._getframe()

    def foo2():
    stack = inspect.stack()
    del stack
    return sys._getframe()

    def bar():
    inspect.stack()
    return sys._getframe()

    frame_foo = foo()
    frame_foo2 = foo2()
    frame_bar = bar()

    print sys.getrefcount(frame_foo)
    print sys.getrefcount(frame_foo2)
    print sys.getrefcount(frame_bar)

    Output :
    3
    2
    2

    So it seems that there is one more reference to the foo frame because
    only because of "stack = inspect.stack()", so its lifetime isn't done
    contrary to foo2 and bar frame, and the frame id of a foo frame is
    different for each call.

    Now, what is keeping a reference on foo frame?

    Thanks Alf,

    Felix
    Félix-Antoine Fortin, Mar 12, 2010
    #3
  4. En Thu, 11 Mar 2010 21:03:07 -0300, Félix-Antoine Fortin
    <> escribió:
    > On Mar 11, 6:22 pm, "Alf P. Steinbach" <> wrote:
    >> * F lix-Antoine Fortin:
    >>
    >> > Given this code :
    >> > # Experience with frame
    >> > import sys
    >> > import inspect

    >>
    >> > def foo():
    >> > stack = inspect.stack()
    >> > print "foo frame : " + str(hex(id(sys._getframe())))

    >>
    >> > def foo2():
    >> > inspect.stack()
    >> > print "foo2 frame : " + str(hex(id(sys._getframe())))

    >>
    >> > def bar():
    >> > print "bar frame : " + str(hex(id(sys._getframe())))

    >>
    >> > foo()
    >> > foo()

    >>
    >> > foo2()
    >> > foo2()

    >>
    >> > bar()
    >> > bar()

    >>
    >> > Output example :
    >> > foo frame : 0x84d2c0
    >> > foo frame : 0x844bf0
    >> > foo2 frame : 0x898c90
    >> > foo2 frame : 0x898c90
    >> > bar frame : 0x898f70
    >> > bar frame : 0x898f70

    >>
    >> > Why are the ids (address) of the frame for each foo call not the same?

    >>
    >> You're dealing with Python objects. You're not dealing with the
    >> computer's
    >> machine stack. Whether you get the same id for two objects whose
    >> lifetimes don't
    >> overlap depends on the implementation's memory and id allocation
    >> strategy.
    >>

    >
    > Okay, I thought I got that when I read the id documentation, but now I
    > get it.
    > So the only to compare two ids, is by making sure their lifetimes
    > overlap. In
    > this case, instead of keeping the ids, I have to keep a reference on
    > the frame
    > to make sure it is still alive when I will compare it with a second
    > one.


    The best way to compare object identities is using the 'is' operator: `a
    is b` returns true if and only if after evaluating both operands they are
    the very same object.
    id() may be misleading if you are not careful:

    py> id([]) == id([])
    True
    py> [] is []
    False

    >> > Or why the call to "stack = inspect.stack()" change the address of the
    >> > frame?

    >>
    >> Does it?

    >
    > Yeah it does... I always get N different id when I run foo() N times
    > in a row.


    Think again after reading the response below.

    > Actually, what you said about lifetime applies here too. Here is
    > another quick
    > snippet :
    >
    > import sys
    > import inspect
    >
    > def foo():
    > stack = inspect.stack()
    > return sys._getframe()
    >
    > def foo2():
    > stack = inspect.stack()
    > del stack
    > return sys._getframe()
    >
    > def bar():
    > inspect.stack()
    > return sys._getframe()
    >
    > frame_foo = foo()
    > frame_foo2 = foo2()
    > frame_bar = bar()
    >
    > print sys.getrefcount(frame_foo)
    > print sys.getrefcount(frame_foo2)
    > print sys.getrefcount(frame_bar)
    >
    > Output :
    > 3
    > 2
    > 2
    >
    > So it seems that there is one more reference to the foo frame because
    > only because of "stack = inspect.stack()", so its lifetime isn't done
    > contrary to foo2 and bar frame, and the frame id of a foo frame is
    > different for each call.
    >
    > Now, what is keeping a reference on foo frame?


    The foo frame keeps a reference to the 'stack' local variable (in its
    f_locals attribute), and 'stack' keeps a reference to the current frame
    too.
    This doesn't happen neither in foo2() nor bar(), where the local array is
    empty. inspect.stack() isn't special: any other reference to the current
    frame would have the same effect.

    Let's examine the simple example above:

    py> id([]) == id([])
    True

    Python creates an empty list, takes its id, and discards the list. The
    list object is then ready to be re-used again (the interpreter keeps a
    list of free objects for many common types), so the right-hand side gets
    the very same list.

    The same thing happens with the frame object in your first examples foo2()
    and bar(): the frame object is discarded after leaving the function, and
    is ready to be used again in the next call. But foo() creates a circular
    reference - the frame object is still alive after leaving the first call,
    so the second call must use a new frame. (The garbage collector will,
    eventually, break the cycle and free those objects, but not very soon).

    --
    Gabriel Genellina
    Gabriel Genellina, Mar 12, 2010
    #4
  5. Thanks Gabriel, you resumed quite well what I did discovered after my
    second post
    by playing with the garbage collector module.

    > (The garbage collector will,  
    > eventually, break the cycle and free those objects, but not very soon).


    I'm not very familiar with the Python garbage collector, so you may
    excuse my
    simple question, but how can it break the cycle? I guess the object
    will be
    freed at least when the program ends, but could it be before that? Is
    there a
    mechanisme in the garbage collector to detect circular references?

    Felix
    Félix-Antoine Fortin, Mar 12, 2010
    #5
  6. Félix-Antoine Fortin

    MRAB Guest

    Félix-Antoine Fortin wrote:
    > Thanks Gabriel, you resumed quite well what I did discovered after my
    > second post
    > by playing with the garbage collector module.
    >
    >> (The garbage collector will,
    >> eventually, break the cycle and free those objects, but not very soon).

    >
    > I'm not very familiar with the Python garbage collector, so you may
    > excuse my
    > simple question, but how can it break the cycle? I guess the object
    > will be
    > freed at least when the program ends, but could it be before that? Is
    > there a
    > mechanisme in the garbage collector to detect circular references?
    >

    In CPython objects are reference-counted, which allows an object to be
    collected as soon as there are no references to it.

    However, this won't take care of circular references, so a secondary
    garbage collector was introduced which occasionally looks for
    inaccessible objects (garbage) using (probably) mark-and-sweep.
    MRAB, Mar 12, 2010
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    2
    Views:
    274
  2. Sven
    Replies:
    2
    Views:
    5,087
    tragomaskhalos
    Nov 30, 2007
  3. Surinder Singh
    Replies:
    1
    Views:
    1,181
    Richard Bos
    Dec 20, 2007
  4. Casey Hawthorne
    Replies:
    3
    Views:
    1,070
    Flash Gordon
    Nov 1, 2009
  5. deluxstar
    Replies:
    5
    Views:
    856
    Terry Reedy
    Sep 25, 2010
Loading...

Share This Page