flybonzai
flybonzai

Reputation: 3931

Closures vs. Function Attributes?

For a fun little project I created the following code to practice non-OOP state-retention in functions. I came up with two ways of doing it, and then realized that nesting the inner_fib function in the second example was redundant (seems to function just fine regardless). I have tested all three and gotten the same result.

From a best practice point of view, is one preferred over the other? Func attributes are more concise, but I can see them being confusing to someone maintaining your code later. Here are my examples:

def fib(first=0, second=1, temp=0):
    first = 0
    second = 1
    temp = 0
    def inner_fib():
        nonlocal temp, first, second
        temp = first + second
        first = second
        second = temp
        return first
    return inner_fib


def fib2():
    def inner_fib():
        inner_fib.temp = inner_fib.first + inner_fib.second
        inner_fib.first = inner_fib.second
        inner_fib.second = inner_fib.temp
        return inner_fib.first
    return inner_fib

def inner_fib():
    inner_fib.temp = inner_fib.first + inner_fib.second
    inner_fib.first = inner_fib.second
    inner_fib.second = inner_fib.temp
    return inner_fib.first

res = 0
a = inner_fib
a.first = 0
a.second = 1
a.temp = 0
while True:
    b = a()
    print(b, res + b)
    if b % 2 == 0 and b < 4000000:
        res += b
    elif res + b > 4000000:
        break
print(res)

Upvotes: 1

Views: 97

Answers (1)

fjarri
fjarri

Reputation: 9726

Honestly, neither is preferred. As you admit, the second approach is equivalent to the third one (the nesting is redundant). With the third approach, you have the following disadvantages:

  • inner_fib is not valid before you add some attributes to it — potential source of errors
  • you are monkey-patching an external object — bad style and potential source of errors

The first approach is slightly better, but a mutating closure seems like an overkill. A more recognizable way (and also more compatible with the standard library) would be to use a generator:

def fib(first=0, second=1, temp=0):
    first = 0
    second = 1
    temp = 0

    while True:
        temp = first + second
        first = second
        second = temp
        yield first

# in Python 3:
for _, x in zip(range(5), fib()):
    print(x)

A mutable class is an alternative, depending on how you are going to use this function.

Upvotes: 2

Related Questions