0

Suppose I have the following function that returns a function:

def make_square_matrix_creator(dim):

    mat = np.zeros([dim, dim])

    def square_matrix_creator(value):
        mat += value
        return mat
    return square_matrix_creator

Now, this code doesn't work, because the internal function can't access mat.

f = make_square_matrix_creator(4)

f(3)

UnboundLocalError: local variable 'mat' referenced before assignment

I know there are a few ways to get around this; I can make mat global:

def make_square_matrix_creator(dim):

    global mat
    mat = np.zeros([dim, dim])

    def square_matrix_creator(value):
        global mat
        mat += value
        return mat
    return square_matrix_creator

It works, but this has all of the problems associated with making global objects within functions

I can pass mat as a default argument to the internal function;

def make_square_matrix_creator(dim):

    mat = np.zeros([dim, dim])

    def square_matrix_creator(value, mat=mat):
        mat += value
        return mat
    return square_matrix_creator

But when I try this out in my real-world example, I run into problems with mutable defaults. Are there other options for giving an internal function access to objects created in its parent function?

C_Z_
  • 7,427
  • 5
  • 44
  • 81

2 Answers2

0

For Python 2.7, you can use [:] to indicate in-place mutation in the nested function. Example -

def make_square_matrix_creator(dim):
    mat = np.zeros([dim, dim])
    def square_matrix_creator(value):
        mat[:] = mat + value
        return mat
    return square_matrix_creator

I tested this in Python 3.4 (Sadly, I don't have a Python 2.7 with numpy to test it out , I tested in Python 2.7 with normal lists and it works for normal lists) . Demo -

In [50]: def make_square_matrix_creator(dim):
   ....:         mat = np.zeros([dim, dim])
   ....:         def square_matrix_creator(value):
   ....:                 mat[:] = mat + value
   ....:                 return mat
   ....:         return square_matrix_creator
   ....:

In [53]: f = make_square_matrix_creator(4)

In [54]: f(3)
Out[54]:
array([[ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.],
       [ 3.,  3.,  3.,  3.]])
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
0

You can try creating an alias.

# This class is a minimally working shim for the numpy object
# because I don't want to import the full numpy package.
class K(object):
    def __init__(self, other):
        self.v = other

    def __iadd__(self, other):
        self.v += other

def outer(x):
    mat = K(x)
    def inner(y):
        alias = mat   # Create alias to mutable object.  alias is now modifiable
        alias += 1    # Modify alias to work around Python 2.x limitation.
        return mat
    return inner

>>> f = outer(5)
>>> res = f(1)
>>> res
<__main__.K at 0x102acb1d0>
>>> res.v
6
>>> f(1)
<__main__.K at 0x102acb1d0>
>>> res.v
7
Cong Ma
  • 10,692
  • 3
  • 31
  • 47