2

Here is the code that i wrote to get sum of digits from a number repeated number of times till the sum gets below 10:

T = int(input())
for i in range(T):
    N = int(input())
    def P():
            M = [int(d) for d in str(N)]
            N = sum(M)
            if N<10:
                    print(N)
            else :
                    return P()
    P()

On running this code it gives me an error like:

 Traceback (most recent call last): 
 File"C:/Users/AdityaShrivastava/AppData/Roaming/Python/Python36/Scripts/tes 
 ting.py", line 11, in <module>
 P()
 File "C:/Users/Aditya 
 Shrivastava/AppData/Roaming/Python/Python36/Scripts/testing.py", line 5, in 
 P
 M = [int(d) for d in str(N)]
 UnboundLocalError: local variable 'N' referenced before assignment
Aditya Shrivastava
  • 267
  • 1
  • 3
  • 11

4 Answers4

1

Functions in python declare a new scope, which is why N is not visible in your function. You can circumvent this by passing N along into the inner scope, like this:

T = int(input())
for i in range(T):
    N = int(input())
    def P(N):
            M = [int(d) for d in str(N)]
            N = sum(M)  # this line was the actual culprit
            if N<10:
                    print(N)
            else :
                    return P(N)
    P(N)

>>> 1      # test one time
>>> 12345  # cross total of 121212
6

Python is conservative on write-operations. Only reading from an outer-scope variable is fine, but re-assigning the name N (which is what happens if you write N = sum(M)) makes it check strictly for a local variable of that name.

As a consequence, it further assumes that that not-yet-declared variable is where you want to read from in the line above it - which is honestly a bit misleading.


For further information on scopes and namespaces in python, check here.

Arne
  • 17,706
  • 5
  • 83
  • 99
1

The UnboundLocalError: local variable 'N' referenced before assignment you are getting is as a result of using N inside function P() without having declared and initialized it.

N = int(input()) is inside the loop but outside the scope of P(). The last line P() in the loop will call function P() and not go back to N = int(input()) where N would have been assigned.

I have modified the code to

T = int(input())
for i in range(T):
    N = int(input())
    def P(N):
        M = [int(d) for d in str(N)]
        N = sum(M)
        if N<10:
            print(N)
        else :
            return P(N)
    P(N)
k.wahome
  • 962
  • 5
  • 14
1

You can fix this by passing N as a parameter in the P function.

T = int(input())
for i in range(T):
N = int(input())
def P(n):
        M = [int(d) for d in str(n)]
        n = sum(M)
        if n<10:
                print(n)
        else :
                P(n)
P(N)

If you set value of N inside the function P, python understands it as creating a local variable with that name. This local variable masks the global variable N used outside of the function. So, you better pass N as a parameter to function P.

Thanks.

Anubhav Singh
  • 8,321
  • 4
  • 25
  • 43
1

You are using recursion to solve this. It is more efficient to simply use a loop:

def gimmeNumber(text):
    """"Helper: Asks for input until valid integer number is inputted. Retuns the number"""
    while True:
        T = input(text).strip()
        if T and T.isdigit():
            T = int(T)
            break
        print("Thats not a number ...")
    return T

def sumDigits(number):
    return sum(int(x) for x in str(number))

T = gimmeNumber("How many numbers? ")
for _ in range(T):
    s = 0
    N = gimmeNumber("Give me a number: ")
    # calculate the cross-sum
    s = sumDigits(N)
    while s > 9: # repeat while greater then 9  
        s = sumDigits(s)

    print(s)

Input: 4, then 999,888,333,111

Output:

9
6
9
3

As @Arne suggested changing gimmeNumber(text) to use try/except instead fits better into pythons Ask forgiveness not permission mindset, and I agree.

Still, the variant above also works and is easier to understand for beginners. Here is the try/except one:

def gimmeNumber(text):
    """"Helper: Asks for input until valid integer number is inputted. Retuns the number"""
    while True:
        try:
            T = int(input(text).strip())
            break
        except ValueError as e:
            print("Thats not a number ...")
    return T

For more about input validation I would suggest reading up on it in the answers of Asking the user for input until they give a valid response .

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • This is probably the best solution since it avoids running into the scoping problem in the first place. – Arne Aug 26 '18 at 13:07
  • 1
    It's more pythonic to handle the type conversion in `gimmeNumber` with a `try..except` instead of an `if`. – Arne Aug 26 '18 at 13:08
  • 1
    @Arne you are right, editied. Still providing both as I feel the first version is easier to understand for beginner coders. – Patrick Artner Aug 28 '18 at 05:41