What's the advantage of using this piece of code in python ? (set i = [0] first, and later i[0] += size)

107 Views Asked by At

I'm reading a python script with the module Theano used. I'm confused by the follow piece of code.

import numpy as np 
import theano.tensor as T
lparam = T.dvector('lparam')  # packed parameters
func = lambda expr: (theano.function([lparam],expr),expr)

i = [0]
def read_param(size):
    ret = lparam[i[0]:i[0]+size]
    i[0] += size
    return ret

The function read_param is used in functions beneath.

N = 20
unpack_param, unpack_param_expr = func(T.stack(
    read_param(N),  # sigmoid energy
    read_param(N),  # sigmoid switching z
    T.exp(read_param(N)),  # sigmoid scale (>0)
    read_param(N),  # gaussian energy
    read_param(N),  # gaussian location
    T.exp(read_param(N))))  # gaussian scale (1/2sigma**2)


def pack_param(param):
    return np.concatenate((
        param[0],
        param[1],
        np.log(param[2]),
        param[3],
        param[4],
        np.log(param[5])), axis=0)

I was told that it uses the concept of "closure" and is advantageous in writing and reading. But I'm not sure why and how it works.

2

There are 2 best solutions below

2
On

My only guess is that the author didn't want to define i as a global variable:

i = 0
def read_param(size):
    global i
    ret = lparam[i:i+size]
    i += size
    return ret

I don't know why (maybe the author doesn't know Python well).

There is no advantage in using i[0], only disadvantages -- bad readibility and more CPU used.

The trick here is that i is a mutable object -- list and you mutate it instead of reassigning (which would require to define it as global). I would use a more pythonic approach like in @Marius's answer.

I think the author considers this a cool trick (though this one is cooler: def read_param(size, i=[0]):) and will defend his approach, but The Zen of Python should be followed:

  • Explicit is better than implicit.
  • Simple is better than complex.
  • Readability counts.
  • Special cases aren't special enough to break the rules.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one-- and preferably only one --obvious way to do it.
0
On

I'm struggling to see the advantages of the current code. i is created as a list so it can be mutated within the function, but as warvariuc shows, you could just use global to achieve the same thing.

Here is how I would do the same thing "pythonically":

class ParamReader(object):
    def __init__(self, params):
        self.params = params
        self.i = 0

    def read(self, size):
        ret = self.params[self.i:(self.i + size)]
        self.i += size
        return ret

# Dummy values for lparam as I don't have theano
lparam = list(range(100))
reader = ParamReader(lparam)

reader.read(5)
Out[8]: [0, 1, 2, 3, 4]

reader.read(6)
Out[9]: [5, 6, 7, 8, 9, 10]

The read function has to maintain state, so it seems like an obvious choice to just use a simple class.