3

I'm a student doing a computer science course and for part of the assessment we have to write a program that will take 10 digits from the user and used them to calculate an 11th number in order to produce an ISBN. The numbers that the user inputs HAVE to be limited to one digit, and an error message should be displayed if more than one digit is entered. This is the code that I am using:

print('Please enter your 10 digit number')
a = int(input("FIRST NUMBER: "))
aa = (a*11)
if len(a) > 1:
    print ("Error. Only 1 digit allowed!")
b = int(input("SECOND NUMBER: "))
bb = (b*10)
if len(a) > 1:
    print ("Error. Only 1 digit allowed!")

ect.

I have to keep the inputs as integers so that some of the calculations in the rest of the program work, but when I run the program, an error saying "object of type 'int' has no len()". I'm assuming that it is referring to the fact that it is an integer and has no length. Is there any way that I can keep 'a' as an integer but limit the length to 1 digit?

(Also I understand that there is probably a more efficient way of writing the program, but I have a fairly limited knowledge of python)

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
Kai McClymont
  • 55
  • 1
  • 1
  • 5
  • `aa = (a*11)` and `bb = (b*10)` are totally bogus, they force user to input one single digit then multiply whatever that digit is by 10 or 11; instead of iterating 10 or 11 times on the call to `input()` – smci Apr 15 '15 at 16:58

5 Answers5

1

You have to convert the int to a string because int does not have a length property. Also You were checking if the digit was longer than 1 for a twice so I switched the SECOND NUMBER check to b

print('Please enter your 10 digit number')
a = raw_input("FIRST NUMBER: ")
if len(a) > 1:
    print ("Error. Only 1 digit allowed!")
a = int(a)
aa = (a*10)

b = raw_input("SECOND NUMBER: ")
if len(b) > 1:
    print ("Error. Only 1 digit allowed!")
b = int(b)
bb = (b*10)

Or more simply:

You could ask for the number and keep asking until the length is 10 and the input is a number

num = raw_input('Please enter your 10 digit number:')
while len(num) != 10 or (not num.isdigit()):
    print 'Not a 10 digit number'
    num = raw_input('Please enter your 10 digit number:')
num = int(num)
print 'The final number is: ', num
heinst
  • 8,520
  • 7
  • 41
  • 77
  • 1
    Might be better to check before converting the input to `int` - all this `str(int(str))` stuff is a bit silly. – a p Apr 15 '15 at 16:54
  • 1
    `aa = (a*11)` and `bb = (b*10)` are bogus, they force user to input one digit then multiply it by 10 or 11; instead of iterating 10 or 11 times on the call to `input()` – smci Apr 15 '15 at 16:58
  • @smci I was wondering why he put that in there. I added a more simple solution too – heinst Apr 15 '15 at 17:03
0

Firstly, I'm assuming you are using 3.x. Secondly, if you are using 2.x, you can't use len on numbers.

This is what I would suggest:

print('Please enter your 10 digit number')

number = ''

for x in range(1,11):
    digit = input('Please enter digit ' + str(x) + ': ')
    while len(digit) != 1:
        # digit is either empty or not a single digit so keep asking
        digit = input('That was not 1 digit. Please enter digit ' + str(x) + ': ')        
    number += digit # digit is a single digit so add to number

# do the rest

It makes more sense to keep all the numbers in a str as you can then split them out later as you need them e.g. number[0] will be the first digit, number[1] will be the second.

If you can adapt your program to not have to explicitly use a, b, c ,d etc. and instead use slicing, it will be quite simple to construct.

Obviously if you can use a whole 10 digit number than the best method would be:

number = input('Please enter your 10 digit number: ')

while len(number) != 10:
    number = input('That was not a 10 digit number. Please enter your 10 digit number ')

As a last resort, if you absolutely have to have individual variable names per digit, you can use exec and eval:

var_names = [('a', 1), ('b', 2), ('c', 3), ('d', 4)] # add more as needed

for var, num in var_names:
    exec(var + ' = input("Please enter digit " + str(num) + ": ")')
    while eval('len(' + var + ')') != 1:
        exec(var + ' = input("That was not a single digit. Please enter digit " + str(num) + ": ")')

That would give you vars a,b,c and d equalling the digits given.

NDevox
  • 4,056
  • 4
  • 21
  • 36
  • 1
    I see you looked at my idea :p – heinst Apr 15 '15 at 17:10
  • @Heinst hah, kind of yes and no. I was going to write it originally, then saw he wanted individual digits and thought not, then thought I should give a complete answer (and realised you had also mentioned it). I can remove and give you credit if you want? I just like having everything in one place. – NDevox Apr 15 '15 at 17:19
  • 1
    nah thats alright i was just messing around I don't care :p – heinst Apr 15 '15 at 17:20
0

You never stated if you're on Windows or Linux, the code listed below is for Windows (as I'm on a Windows machine right now and can't test the equivalent on Linux).

# For windows
import msvcrt
print('Please enter your 10 digit number')
print('First number: ')
a = int(msvcrt.getch())
print(a)

The .getch() call with msvcrt just gets a single character from the terminal input. You should also wrap the call to int() in a try/except block to stop your application from crashing when getting non-integer input.

Christian Witts
  • 11,375
  • 1
  • 33
  • 46
  • 1
    This assumes the user cannot make a mistake which is bad practice – NDevox Apr 15 '15 at 17:02
  • Hence the note to wrap the int call in a try/except block below the code snippet. – Christian Witts Apr 15 '15 at 17:06
  • if non-integer. what if the user enters the wrong int. – NDevox Apr 15 '15 at 17:07
  • They shouldn't really be inputting 1 character at a time, but be allowed to enter the 10 digits of the ISBN, which only then do you process. If you are assuming the user is going to be entering the incorrect values the whole time, then you should have a multi-step process asking them to input the character, then pressing y/n if that was indeed the correct one. If you're saying that because my snippet only has 1 call to `getch()` and not 10, it's because that is an exercise best left up to the OP. – Christian Witts Apr 15 '15 at 17:11
  • My point is a user can enter an incorrect digit, then realise, and delete before pressing enter. with your answer, they can't. – NDevox Apr 15 '15 at 17:20
  • @ChristianWitts nice, but please write the complete code. When you add testing for integerness, you'll also need to add a loop. – smci Apr 15 '15 at 17:58
  • 1
    The question was titled "How can I limit the amount of digits in an input?" with the OP wanting to only read in 1 character at a time, I fail to see how writing a complete application is needed. – Christian Witts Apr 15 '15 at 19:09
0

I suggest creating a function to handle of of the prompting, then call it in your code. Here is a simplfied example:

def single_num(prompt):
    num = ""
    while True:
        num = raw_input(prompt)

        if len(num) == 1:
            try:
                return int(num)
            except ValueError:
                print "Error, you must enter a number"
        else:
            print "Try again, now with a single number"

This will take a prompt, and ask it over and over again until it recieves a 1 length string. To make this more user friendly, you can add in protections with try...except for non-numerical input and whatnot

wnnmaw
  • 5,444
  • 3
  • 38
  • 63
0

This is probably cleanest to do with a validation wrapper.

def validator(testfunc):
    def wrap(func):
        def wrapped(*args, **kwargs):
            result = func(*args, **kwargs)
            pass, *failfunc = testfunc(result)
            it pass:
                return result
            elif failfunc:
                failfunc[0]()
        return wrapped
    return wrap

def ten_digits(num):
    pass = False
    if len(num) != 10:
        msg = "{} is not of length 10".format(num)
    elif not num.isdigit():
        msg = "{} is not a number".format(num)
    else:
        pass = True
    def failfunc():
        raise ValueError(msg)
    response = (pass, None if pass else failfunc)
    return response

valid_input = validator(ten_digits)(input) # or raw_input in Python2
response = valid_input("Enter your 10 digit number: ")

This is probably a bit overengineered, but it's incredibly reusable (have a different set of tests you need validated? Write a new ten_digits analogy!) and very configurable (want different behavior out of your fail function? Write it in!) Which means you could do things like:

ISBN = validator(ten_digits)(input)("ISBN# = ")
title = validator(max_50_chars)(input)("Title = ")
author = validator(no_digits)(input)("Author = ")
price = decimal.Decimal(validator(float_between_1_and_50)(input)(
        "Price = ")).quantize(decimal.Decimal('1.00'))
Adam Smith
  • 52,157
  • 12
  • 73
  • 112