Jess Jess - 1 month ago 13
Python Question

Defining functions inside of other functions

I defined a function inside of a function in a program (Python):

def func1:
x = 5
def func2(y):
return x*y

x = 4
print func2(5)


How does the scope for x here work, with AND without the line that redefines x=4?
Is this language-specific behavior?

Answer

First of all: you're being too lazy. Before you write code here, at least feed it to an interpreter or compiler for the language in question to weed out the obvious problems. In this case, there are two:

  1. Your example code won't compile due to a syntax error. Fixing it by adding () on the definition of func1.
  2. Your example code won't run due to func2 being undefined when it is called.

So let's try

#!/usr/bin/env python

def func1():
   x = 5 
   def func2(y):
      #x = x + 1  # invalid: "x referenced before assignment"
      return x*y  # not invalid! reference to x is fine
   global func3
   func3 = func2

#print func3(5)  # undefined

func1()

print func3(5)
#print func2(5)  # undefined

Three lines are commented; they produce errors when uncommented.

As you can see, Python definitions, of both variables and functions, are local by default: when defined within a function, they only apply within that function, and cannot be used elsewhere.

Variables defined outside a function can be used inside the function. However, they can only be read, not written. This explains why we can use x inside func2 but cannot assign to it; when x = x + 1 is uncommented, it is interpreted as applying to a different x variable inside func2, which is why we'll get an error complaining that it is used uninitialized.

In addition, Python has global variables, which can be used anywhere in the program; such variables must be marked global explicitly.

This is very similar to PHP (in which variables can also be marked as global) and fairly similar to many other languages, such as C (which has no global, but the similar extern).

What you're trying to do with your code is have a function (func2) use a variable (x) defined in the context of its definition, then call it outside the context where x is defined and still have it work. Functions that can do this are known as closures.

We've just seen that Python only has partial support for closures: the function can read such variables, but cannot modify them. This is already better than many other languages. However, a full closure can also modify such variables.

For instance, the following is valid Perl code:

#!/usr/bin/env perl

use strict;
use warnings;

my $func3 = sub { 1 };

my $func1 = sub
{
  my $x = 5;
  my $func2 = sub
  {
    my ($y) = @_;
    ++$x;
    $x*$y
  };
  $func3 = $func2
};

#&$func2(5);  # won't compile: func2 is undefined
print &$func3(5), "\n";  # prints 1
&$func1();
print &$func3(5), "\n";  # prints 30
print &$func3(5), "\n";  # prints 35

Full closure support is traditionally associated with dynamic scoping (i.e. making definitions executable statements, evaluated at run time). This is because it was introduced by Lisp, which was dynamically scoped, and support for it is rare in languages that use static (lexical) scoping (in which the scope of definitions is determined at compile time, not at run time). But all of the variables and function definitions in this Perl code are statically scoped.