简体   繁体   中英

How do I convert an input string to a list of floats in python

I'm making a program that checks if a credit card is valid. I wrote code that checks if the card's number passes Luhn's algorithm but I overlooked one detail at the beginning which is that you can't directly convert a string list into floats. I only realized this when I started testing my program using real card numbers and getting a ValueError. Is there any solution to this without having to reimplement my solution.

def card(digits):
    digits = [int(i) for i in digits]
    digits = [float(x) for x in (digits)]
    print(digits)

    #checks card length and checksum
    if len(digits) == 13:
        even_digits = [digits[-2] * 2, digits[-4] * 2, digits[-6] * 2, digits[-8] * 2, digits[-10] * 2, digits[-12] * 2]
        odd_digits = [digits[-1], digits[-3], digits[-5], digits[-7], digits[-9], digits[-11], digits[-13]]
        all_sum = sum(odd_digits) + sum(even_digits)

        if all_sum % 10 == 0:
            print("checksum passed")

            if digits[0] == 4:
                print("VISA")
        else:
            print("INVALID")

    elif len(digits) == 15:
        even_digits = [digits[-2] * 2, digits[-4] * 2, digits[-6] * 2, digits[-8] * 2, digits[-10] * 2, digits[-12] * 2, digits[-14] * 2]
        odd_digits = [digits[-1], digits[-3], digits[-5], digits[-7], digits[-9], digits[-11], digits[-13], digits[-15]]
        all_sum = sum(odd_digits) + sum(even_digits)

        if all_sum % 10 == 0:
            print("checksum passed")

            if digits[0] == 3 and digits[1] == 7 or 4 :
                print("AMEX")        
        else:
            print("INVALID")

    elif len(digits) == 16:
        even_digits = [digits[-2] * 2, digits[-4] * 2, digits[-6] * 2, digits[-8] * 2, digits[-10] * 2, digits[-12] * 2, digits[-14] * 2, digits[-16] * 2]
        odd_digits = [digits[-1], digits[-3], digits[-5], digits[-7], digits[-9], digits[-11], digits[-13], digits[-15]]
        all_sum = sum(odd_digits) + sum(even_digits)

        if all_sum % 10 == 0:
            print("checksum passed")

            if digits[0] == 4:
                print("VISA")

            elif digits[0] == 5 and digits[1] == 1 or 2 or 3 or 4 or 5:
                print("MASTERCARD")
        else:
            print("INVAlID")

    else:
        print("INVALID")



card(input("Number: "))

you can't directly convert a string list into floats.

I don't know what you think "string list" means, but you can convert strings into either integers or floats according to whether the text in the string looks like a valid integer or floating-point number . A string like "3" converts to the integer 3 or the floating-point value 3.0 ; a string like "apple" doesn't convert to anything numeric because there is no sensible numeric value to convert it to. Similarly in your actual code: you can't convert " " (a single space).

digits = [int(i) for i in digits]

Since digits was a string before, this iterates over each character of the string. If you wanted to iterate over each word , then you need to split it up into words first. If you just wanted to skip whitespace, then you need to do that explicitly too.

But the first thing you have to do is think about the problem . What exactly might your input look like? What exactly do you want to end up with in the list as a result?

You want to use int , not float , because you are going to do integer arithmetic when you implement the algorithm.

When you get an error message that says that something couldn't be converted into something else, you need to think about the problem . Is the error because of what you're trying to convert into ? Or is it because of what you're trying to convert from ? To figure this out, the first step is to check what you're trying to convert from, and see if it makes sense for what you're doing.

There are many other logical errors in your code. For example, if digits[0] == 3 and digits[1] == 7 or 4: does not do what you want . You are better served at this point by following along with a tutorial first, being more careful in general, and thinking more about the problem than by trying to get help from Stack Overflow. Also, write less code at a time . Once you know what the logical steps are to solving your problem, implement each one and make sure it works first before proceeding. In your case, that first step is getting the desired list of digits.

I tried your code and it runs if I enter the card number without the '-' or ' ' between digit groups. You also then don't need to convert to floats.

So I'm assuming you need to get rid of the '-' or ' ' character in the card number entered. If you ask the user nicely to use the '-' only between groups then:

digits = digits.replace('-', '')
digits = [int(i) for i in digits]

will fix that problem.

BTW - Once I fixed that problem it still failed to verify my card number as valid so something else is going wrong in there.

Thanks to Green Cloak Guy, I was able to solve the problem. Below is the updated solution if anyone is willing to try it.

#helper method to check if an object can be casted to another type #thanks to Green Cloak Guy

def is_castable(obj, T): try: T(obj) return True except ValueError: return False

def card(digits): digits = [float(x) for x in digits if is_castable(x, float)]

#checks card length and performs checksum(Luhn's algoritm)
if len(digits) == 13:
    even_digits = [digits[-2] * 2, digits[-4] * 2, digits[-6] * 2, digits[-8] * 2, digits[-10] * 2, digits[-12] * 2] #multipies even digits of the card by 2
    #adds digits of a number greater than 10
    #this is necessary because the checksum requires to add each digit of the even digits
    for even_digit in even_digits:
        if even_digit >= 10:
            ind = even_digits.index(even_digit)
            first_digit = int(even_digit / 10)
            last_digit = even_digit % 10
            even_digits[ind] = float(first_digit + last_digit)

    odd_digits = [digits[-1], digits[-3], digits[-5], digits[-7], digits[-9], digits[-11], digits[-13]]
    all_sum = sum(odd_digits) + sum(even_digits)

    if all_sum % 10 == 0:
        #After passing the checksum if the first digit is 4 then it is a visa card
        if digits[0] == 4:
            print("VISA")            
    else:
        print("INVALID")


elif len(digits) == 15:
    even_digits = [digits[-2] * 2, digits[-4] * 2, digits[-6] * 2, digits[-8] * 2, digits[-10] * 2, digits[-12] * 2, digits[-14] * 2]
    #adds digits of a number greater than 10
    #this is necessary because the checksum requires to add each digit of the even digits
    for even_digit in even_digits:
        if even_digit >= 10:
            ind = even_digits.index(even_digit)
            first_digit = int(even_digit / 10)
            last_digit = even_digit % 10
            even_digits[ind] = float(first_digit + last_digit)        
    odd_digits = [digits[-1], digits[-3], digits[-5], digits[-7], digits[-9], digits[-11], digits[-13], digits[-15]]
    all_sum = sum(odd_digits) + sum(even_digits)

    if all_sum % 10 == 0:
        #After passing the checksum if the card starts with 34 or 37 then it is an amex card
        if digits[0] == 3 and digits[1] == 7 or 4 :
            print("AMEX")            
    else:
        print("INVALID")


elif len(digits) == 16:
    even_digits = [digits[-2] * 2, digits[-4] * 2, digits[-6] * 2, digits[-8] * 2, digits[-10] * 2, digits[-12] * 2, digits[-14] * 2, digits[-16] * 2]
    #adds digits of a number greater than 10
    #this is necessary because the checksum requires to add each digit of the even digits
    for even_digit in even_digits:
        if even_digit >= 10:
            ind = even_digits.index(even_digit)
            first_digit = int(even_digit / 10)
            last_digit = even_digit % 10
            even_digits[ind] = float(first_digit + last_digit)
    odd_digits = [digits[-1], digits[-3], digits[-5], digits[-7], digits[-9], digits[-11], digits[-13], digits[-15]]
    all_sum = sum(odd_digits) + sum(even_digits)

    if all_sum % 10 == 0:
        #After passing the checksum if the first digit is 4 then it is a visa card
        if digits[0] == 4:
            print("VISA")
        #After passing the checksum if card starts with 51-5 then it is a mastercard
        elif digits[0] == 5 and digits[1] == 1 or 2 or 3 or 4 or 5:
            print("MASTERCARD")        
    else:
        print("INVAlID")

else:
    print("INVALID")

card(input("Number: "))`

The idiomatic solution to this type of problem is to try to convert things to floats, and only react if it fails. In this case, it's failing, so we need to put some sort of filter on the list to ignore those failures:

# helper method to check if an object can be casted to another type
# (e.g. calling the constructor you give it on the object you give it, and
# checking whether an error happens)
def is_castable(obj, T):
    try:
        T(obj)
        return True
    except ValueError:
        return False

# add a condition to the list comprehensions to ignore the item if it doesn't
# typecast properly
def card(digits):
    digits = [float(x) for x in digits if is_castable(x, float)]
    ...

It shouldn't be necessary to cast to int first, unless you require that the elements are rounded down. In general float() will accept any string that int() will accept.

Also, make sure that processing digits character-by-character is what you want to do. If you want to tokenize it (eg have the user enter a space-separated list like 2 5 8 13 ) then you might consider calling .split() on the initial digits object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM