Scope (?) question

Discussion in 'Python' started by Peter, Jun 15, 2010.

  1. Peter

    Peter Guest

    I am puzzled by what appears to be a scope issue - obviously I have
    something wrong :)

    Why does this work:

    if __name__ == 'main':
    execfile('test-data.py')
    print data

    and yet this doesn't (I get "NameError: global name 'data' not
    defined"):

    def X():
    execfile('test-data.py')
    print data

    where test-data.py is:

    data = [1,2,3,4]

    I checked help on execfile and could only find the following
    (mystifying) sentence:

    "execfile() cannot be used reliably to modify a function’s locals."

    Thanks
    Peter
     
    Peter, Jun 15, 2010
    #1
    1. Advertisements

  2. Peter

    MRAB Guest

    In the first example 'execfile' is passed globals() by default, which
    'test-data.py' modifies.

    In the second example 'execfile' is passed locals() by default (because
    it's called from within a function), which 'test-data.py' modifies.

    However, Python (well, CPython at least), optimises access to locals
    within functions, which unfortunately means that changes to locals()
    won't actually affect the function's locals.

    Here's an example which tries to change the locals:
    .... x = 0
    .... print "Old value of x:", x
    .... locals()["x"] = 1
    .... print "New value of x:", x
    ....Old value of x: 0
    New value of x: 0


    Compare this with changing globals:
    New value of x: 1
     
    MRAB, Jun 16, 2010
    #2
    1. Advertisements

  3. Peter

    Inyeol Lee Guest

    This is due to CPython's static optimization of local name lookup.
    Dummy 'exec' statement disables this and makes your example work:

    def X():
    exec "None"
    execfile('test-data.py')
    print data

    --inyeol
     
    Inyeol Lee, Jun 16, 2010
    #3
  4. Peter

    Peter Guest

    This one seems to do the trick - thanks! :)

     
    Peter, Jun 16, 2010
    #4
  5. Peter

    Peter Otten Guest

    You may also consider the following alternative:

    def f():
    ns = {}
    execfile('test-data.py', ns)
    print ns["data"]

    f()

    This has the advantage that it can be automatically converted to Python 3.

    Peter
     
    Peter Otten, Jun 16, 2010
    #5
  6. What is mystifying about it? It's short and clear -- execfile cannot be
    used to reliably modify a function's local variables.

    The *reason* why this is so is complicated, but the fact that it is so is
    plain and simple, as you already discovered.
     
    Steven D'Aprano, Jun 16, 2010
    #6

  7. Is this documented anywhere? It looks like a nasty hack that is liable to
    disappear at any time. In fact, it looks like a nasty hack which *has*
    disappeared: it doesn't work in Python 3.1:

    .... exec("None")
    # execfile doesn't exist any longer
    .... exec(open('test.py').read())
    .... print(x)
    ....Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 4, in f
    NameError: global name 'x' is not defined


    By experimentation in Python 2.5, it seems to me that it only works if
    the local variable hasn't already been defined. If it has, you can't
    modify it:
    .... x = 1
    .... exec("None")
    .... execfile('test.py')
    .... print x
    ....1


    But you can use it to define new locals:
    .... exec("None")
    .... execfile('test.py')
    .... print x
    ....2
     
    Steven D'Aprano, Jun 16, 2010
    #7
    1. Advertisements

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.