Using 'exec' in subroutines 
Author Message
 Using 'exec' in subroutines

Quote:
Jean-Guy Schneider writes:
> While I was experimenting with a few small python examples, I ran into an
> interesting problem using 'exec': when I run both scripts indicated below
> (they are in the Python book by the way) in toplevel, they both work. As soon
> as I define my own subroutine which does nothing else than calling 'exec', the
> first example runs without any problems, the second crashes with the error

> [...]

> I assume that there is a problem with namespaces, but I was not able to
> figure out what exactly goes wrong.

> [...original examples attached below...]

You're right: it sounds like you've run into Python's 3-scope namespace
lookup rule.  The short story is that name "Tkinter" lives in a scope
which is not accessible inside the class's method.  The long story
is more subtle...

In terms of namespaces, when you call function "myexec", the script works
as though it were textually inserted into the function at the place you
call "exec".  Without arguments, "exec" runs in the current scope.  The
upshot is that the "class" statement is nested in the function, which is
in turn nested in the enclosing module, as though you were calling this:

  def myexec (script)                                 # module scope
      import Tkinter                                  # function scope

      class Hello (Tkinter.Frame):
          def __init__ (self, master=None):           # class scope
              Tkinter.Frame.__init__ (self, master)   # method scope

Now, the "import Tkinter" is run in the function's local namespace, as
is the nested "class" statement.  So far so good, but remember that names
in a class method only have access to names in the method function (local),
in the surrounding module (global), and in the built-in names scope, per
the 3-scope lookup rule.

So, when the nested class's "__init__" runs, it doesn't have access to the
name "Tkinter": it lives in the function enclosing the class (in myexec's
local scope).  Python can't find it in the method itself, the surrounding
module, or the built-in names scope, so you get an exception.  But the
"exec" run outside the function works because it runs in the module's
scope--"Tkinter" is then a global name when referenced in the method.

An interesting example, to say the least :-).  But Python's 3-scope rule
(and the behavior of "exec") is a very common source of confusion when
people first start using Python.  On the other hand, it's a simple rule,
and works the same no matter how much nesting you code (and prevents some
of the surprises I still remember getting in Pascal).

There are a number of ways to fix the problem (assuming you still need to).
Since the examples are coded for use at the top-level of a module, you need
to change the scripts or the "exec" to nest them in a function.  Options:

1) Make sure a "import Tkinter" appears in the module enclosing "myexec",
   before you call it.  This way, "Tkinter" is found in the module (global)
   scope when referenced in the nested method.

      import Tkinter
      def myexec (script):
          exec (script)

2) Do a "import Tkinter" inside your class method; this scheme makes
   "Tkinter" a local name in the method itself.

      import Tkinter
      class Hello (Tkinter.Frame):
          def __init__ (self, master=None):
              import Tkinter
              Tkinter.Frame.__init__ (self, master)
              ...

3) The exec form "exec script in namespace, namespace" lets you pass
   in namespace dictionaries explicitly.  If you pass in the surrounding
   module's namespace dictionary, your scripts would probably work as is;
   use the globals() built-in, and/or the module.__dict__ attribute.

      def myexec (script):
          exec script in globals(), globals()

4) Default arguments might be used to pass the Tkinter module in from
   the class level if imported there.

      import Tkinter
      class Hello (Tkinter.Frame):
          import Tkinter
          def __init__ (self, master=None, Tkinter=Tkinter):
              Tkinter.Frame.__init__ (self, master)

5) And lots of other ways which require more creativity than I have
   time for at the moment :-).

Hope I answered the right question.  By the way, the 3-scope rule is
covered in detail in part-2 of "Programming Python" (see chapter-6).

Mark L.

-----

Quote:

> While I was experimenting with a few small Python examples, I ran into an
> interesting problem using 'exec': when I run both scripts indicated below
> (they are in the Python book by the way) in toplevel, they both work. As soon
> as I define my own subroutine which does nothing else than calling 'exec', the
> first example runs without any problems, the second crashes with the error
> message:

>         raceback (innermost last):
>           File "testscr.py", line 31, in ?
>             myexec (script)
>           File "testscr.py", line 15, in myexec
>             exec (script)
>           File "<string>", line 16, in ?
>           File "<string>", line 7, in __init__
>         NameError: Tkinter

> When I import the 'Tkinter' in the toplevel, the second script works again!

> I assume that there is a problem with namespaces, but I was not able to
> figure out what exactly goes wrong. Is there anybody out there who can help
> me? Thanks.

> #----------------------------------------------------------------------------#

> # Subroutine
> def myexec (script)
>     exec (script)

> # Main program
> # ... reading script from file ...
> exec (script)    # This does work
> myexec (script)  # This does not always work

> #----------------------------------------------------------------------------#

> # Example 2-9 (on page 25)

> from Tkinter import *
> widget = Label (None, text='Hello, World!')
> widget.pack()
> widget.mainloop()

> #----------------------------------------------------------------------------#

> # Example 2-11 (on page 26)

> import Tkinter

> class Hello (Tkinter.Frame):
>     def __init__ (self, master=None):
>         Tkinter.Frame.__init__ (self, master)
>         self.pack()
>         self.make_widgets()

>     def make_widgets (self):
>         widget = Tkinter.Button (self,
>                                  text='Hello, World!', command=self.quit)
>         widget.pack (side=Tkinter.LEFT)

> if __name__ == '__main__': Hello().mainloop()

> #----------------------------------------------------------------------------#



Fri, 11 Feb 2000 03:00:00 GMT  
 
 [ 1 post ] 

 Relevant Pages 

1. Using '*'in exec command on UNIX

2. PLEASE HELP re: making Windows App's using Fortran subroutines

3. Using import in an exec'ed string

4. can't pass environment vars using exec

5. 'dim' parameter in my own subroutines

6. subroutine 'allocate'

7. Problem with 'me' in subroutine

8. fortran code for 'cp' / subroutine file_copy

9. EXEC CICS DOCUMENT INSERT 'AT' parameter

10. g95: installation problem, cannot exec 'f951': No such file or directory

11. need exit from 'exec' execution

12. Bug in 'exec'

 

 
Powered by phpBB® Forum Software