How to reset a variable assigned to function in Python 2.73?

2.5k Views Asked by At

so basically a guy helped me improve my code. The problem is, it's still dead disappointing and does not work. What I wanna do, is reset the lenRecur.number so I could use the function again, using other strings and getting correct answers (not too big answers)

I guess, the problem is with hasattr. But I can't remove it, because if I do, my string length calculator won't work.

Anyway, even if I add lenRecur.number = 0 after the function, it still doesn't work.

It's like impossible, because when the function hits "return", it's done, period. If I reset it before "return", it will return 0, not correct answer so yeah I'm in deep trouble here.

def lenRecur(aStr):
    if not hasattr(lenRecur, 'number'):
        lenRecur.number = 0
    '''
    aStr: a string

    returns: int, the length of aStr
    '''
    if aStr == '':
        return lenRecur.number
    else:
        lenRecur.number += 1
        return lenRecur(aStr[:-1])

P.s. the goal of my program(?) / script(?) is to measure the length of input string, without using input() method. As trying to re-create the length() method, with using more primitive means.

The script will have to have many different inputs, so it shall reset.

3

There are 3 best solutions below

0
On

You don't have to use a state variable inside the function. If you want to make a recursive length calculator, then just do

def lenRecur (aStr):
    if (aStr == ""):
        return 0
    else
        return lenRecur (aStr [:-1]) + 1

Also note that this style has no error checking etc, but for the purposes of learning about recursion, it works fine.

1
On

If you just want a recursive strength-length function, that's easy:

def len_recur(a_str):
    if not a_str:
        return 0
    else:
        return 1 + len_recur(a_str[1:])

Of course that's not tail-recursive, but then Python doesn't optimize tail recursion anyway, so it doesn't matter.

And if you want it to be tail recursive just for the hell of it—or because you've read Paul Butler's trick for Tail Recursion in Python and want to try it out—you still don't want to do that by storing the accumulator as an attribute of the function. Just use the usual trick of defining a local function (or using a mutable default parameter, if you prefer):

def len_tail_recur(a_str):
    def tail(a_str, acc):
        if not a_str:
            return acc
        else:
            return tail(a_str[1:], acc+1)
    return tail(a_str, 0)

If you want to convert this to a real tail-recursive function so it doesn't bomb on lists of 1001 elements, and don't understand the Paul Butler link above, see my answer to Get length of list in Python using recursion, which solves exactly this problem. (Another answer to that question also shows how to solve the problem with log N recursive calls instead of N, which is a different way of getting around the problem, unless you have impossibly long lists.)

All that being said, even though your implementation is the wrong way to do it, it actually works just fine. (So far, I've been PEP8-ifying your code to look more like idiomatic Python; from here on I'm just going to copy-paste as-is, but your real code should look like the above.)

def lenRecur(aStr):
    if not hasattr(lenRecur, 'number'):
        lenRecur.number = 0
    '''
    aStr: a string

    returns: int, the length of aStr
    '''
    if aStr == '':
        return lenRecur.number
    else:
        lenRecur.number += 1
        return lenRecur(aStr[:-1])

print lenRecur('abc')
lenRecur.number = 0
print lenRecur('abcd')

This prints 3, then 4. Of course you have to set lenRecur.number from outside the function, because inside the function you still need the value. But you can solve that with the same kind of wrapper:

def lenRecur(aStr):
    lenRecur.number = 0
    '''
    aStr: a string

    returns: int, the length of aStr
    '''
    def recur(aStr):
        if aStr == '':
            return lenRecur.number
        else:
            lenRecur.number += 1
            return recur(aStr[:-1])
    return recur(aStr)
0
On

If you're trying to understand recursion by implementing the length function using recursion then you can use something like this:

#!python
def lenRecur(something, curlen=0):
    if something:
        return lenRecur(something[1:], curlen+1)
    else:
        return curlen

... I won't claim that this is particularly good code. But it should work with any sort of sequence (string, list, tuple) ... anything for which the [1:] slice operation will work so long as it doesn't exceed the maximum recursion limit in your running Python instance.

In your example you're trying to implement a similar concept by using hasattr to "monkey patch" your function's object with a "number" attribute. In my example I'm using a default parameter as a way of passing the variable down into the recursive calls.

So in the initial call curlen is zero (calling this with the "optional" extra argument would give bogus results). From that call the function calls itself with a slice of the original sequence (string) that lops off the head (making it one shorter) and with the optional (curlen) argument incremented. At the end the string/sequence is of zero length, the zero is returned up through each of the previous (recursive) calls.

It's a lame way to accomplish this, and it could be a starting point for a discussion on tail recursion elimination (Google for it). But it will work without monkey patching your function/object.