Stagnant Frame Data?

Mike loftusoft at yahoo.com
Wed Nov 18 11:10:09 CET 2009


On Nov 15, 1:36 pm, Terry Reedy <tjre... at udel.edu> wrote:
> Peter Otten wrote:
> > Mike wrote:
>
> >> I'll apologize first for this somewhat lengthy example. It does
> >> however recreate the problem I've run into. This is stripped-down code
> >> from a much more meaningful system.
>
> >> I have two example classes, "AutoChecker" and "Snapshot" that evaluate
> >> variables in their caller's namespace using the frame stack. As
> >> written, the output is not what is expected: the variables evaluate to
> >> "stagnant" values.
>
> >> However, if the one indicated line is uncommented, then the result is
> >> as expected.
>
> >> So my questions are: Is this a bug in Python?
>
> No. The existence and use of sys._getframe -- notice the leading
> underscore -- is a CPython implementation artifact. Use at your own risk.
>
>  >> Is this an invalid use of frame data?
>
> Up to you to decide.
>
>  >> Why does the single line "sys._getframe(1).f_locals"
>
> >> fix the behavior?
>
> It updates the dictionary.
>
>
>
> > A simplified demonstration of your problem:
>
> >>>> def f(update):
> > ...     a = locals()
> > ...     x = 42
> > ...     if update: locals()
> > ...     print a
> > ...
> >>>> f(False)
> > {'update': False}
> >>>> f(True)
> > {'a': {...}, 'x': 42, 'update': True}
>
> > The local namespace is not a dictionary, and the locals()/f_locals
> > dictionary contains a snapshot of the local namespace. Accessing the
> > f_locals attribute is one way to trigger an update of that snapshot.
>
> > What's puzzling is that the same dictionary is reused.
>
> Efficiency? Consistency? The doc for locals says "locals()
> Update and return a dictionary representing the current local symbol
> table." In class statements, where (currently) the local namespace *is*
> a dict, no update is needed and locals() simply returns the dict, the
> same one each time.
>
> Terry Jan Reedy

Thanks for the replies.

If Peter's concise and much-appreciated example were to be modified as
such:

>>> def f(update):
   .....:     a = inspect.stack()[0][0].f_locals
   .....:     x = 42
   .....:     if update:
   .....:         inspect.stack()[0][0].f_locals
   .....:     print a
   .....:
>>> f(False)
{'update': False}
>>> f(True)
{'a': {...}, 'x': 42, 'update': True}

Now there's no use of locals() to perform its documented "Update", but
just a simple access of a dictionary. This behavior is curious.

Further modification by accessing 'a' instead of '...f_locals' upon
update produces:

>>> f(False)
{'update': False}
>>> f(True)
{'update': True}

Checking the id() of 'a' and '...f_locals' shows the same "address".

How is it that '...f_locals' gets updated in this example? Is it done
in "stack()"?

Where exactly is the frame data stored prior to updating the
dictionary and can I get to it?

Thanks,
Mike



More information about the Python-list mailing list