Style
Python has a style guide called PEP 8. It is a good habit to try to follow it.
In your case, that would mean fixing the missing whitespaces, removing parenthesis in if(result)
.
Also, you could get rid of old commented code and add a proper docstring if you want to describe the behavior of the function.
Tests
It could be a good idea to write tests before trying to improve your code.
I wrote a very simple code but you could get this chance to dive into unit testing frameworks:
TESTS = [
(1456734512345698, False),
(4539869650133101, True),
(1456734512345698, False),
(5239512608615007, True),
]
for (card_number, expected_valid) in TESTS:
valid = validate_credit_card_number(card_number)
assert valid == expected_valid
Useless test
At the end of the function, you can return directly:
return final_sum % 10 == 0
Useless variables
The my_list
variable is not required.
Also, we can take this chance to get rid of the variables at the end of the function:
return (sum(t_list) + sum(list2)) % 10 == 0
Conversion of card_number
At the beginning of the function, you could convert the parts of card_number
directly to integer so that you do it in a single place.
Also, that removed the need to the call of list
. We just have:
temp_list = [int(c) for c in str(card_number)]
And we can get rid of the line:
list2 = [int (n) for n in list2]
At this stage, the code looks like:
def validate_credit_card_number(card_number):
temp_list = [int(c) for c in str(card_number)]
list1 = temp_list[-2::-2]
list1 = [2 * n for n in list1]
list2 = temp_list[::-2]
t_list = list1
for el in list1:
sum_res = 0
if el > 9:
idx = list1.index(el)
t_list.pop(idx)
while el:
rem = el % 10
sum_res += rem
el = el // 10
t_list.insert(idx, sum_res)
return (sum(t_list) + sum(list2)) % 10 == 0
Yet another useless variable
t_list
aliases list1
(I am not sure if this is intended if if you were planning to have a copy of list1
). Whenever you update the list through one variable, the other is affected as well. I highly recommend Ned Batchelder's talk about names and values.
In your case, we can get rid of t_list
completely without changing the behavior of the function.
Simplify list logic
You go through multiple steps to modify list1
(or t_list
) : index
, pop
, index
. These steps are more expensive/complicated than required. At the end of the day, you do not care about list1
, you just want its final sum. You could keep track of the sum directly:
sum1 = 0
for el in list1:
if el > 9:
sum_res = 0
while el:
rem = el % 10
sum_res += rem
el = el // 10
sum1 += sum_res
else:
sum1 += el
return (sum1 + sum(list2)) % 10 == 0
We can take this chance to perform the multiplication in the loop to remove a list comprehension.
Also, we can initialise the sum with sum(list2)
so that we don't have to add them at the end:
def validate_credit_card_number(card_number):
temp_list = [int(c) for c in str(card_number)]
list1 = temp_list[-2::-2]
list2 = temp_list[::-2]
total_sum = sum(list2)
for el in list1:
el *= 2
if el > 9:
sum_res = 0
while el:
rem = el % 10
sum_res += rem
el = el // 10
total_sum += sum_res
else:
total_sum += el
return total_sum % 10 == 0
Math logic
The code uses 10 (the base used for computations) everywhere except for one 9 which seems unexpected. You could write: el >= 10
instead.
Also, that check is not required because the logic applies exactly the same way for elements smaller than 10:
for el in list1:
el *= 2
sum_res = 0
while el:
rem = el % 10
sum_res += rem
el = el // 10
total_sum += sum_res
Also, you could use el //= 10
but you can get the best ouf of the Python builtins by using divmod
returning both the quotient and the remainder:
while el:
el, rem = divmod(el, 10)
sum_res += rem
total_sum += sum_res
Then, it becomes clear that the variable sum_res
is not really required as we could use total_sum
instead:
while el:
el, rem = divmod(el, 10)
total_sum += rem
"Final" code
def validate_credit_card_number(card_number):
temp_list = [int(c) for c in str(card_number)]
list1 = temp_list[-2::-2]
list2 = temp_list[::-2]
total_sum = sum(list2)
for el in list1:
el *= 2
while el:
el, rem = divmod(el, 10)
total_sum += rem
return total_sum % 10 == 0
TESTS = [
(1456734512345698, False),
(4539869650133101, True),
(1456734512345698, False),
(5239512608615007, True),
]
for (card_number, expected_valid) in TESTS:
valid = validate_credit_card_number(card_number)
assert valid == expected_valid
More simplification
Thinking about it, things can still be simplified a lot.
What you are doing with the while
loop can be performed using str
conversion:
total_sum = sum(list2)
for el in list1:
total_sum += sum(int(c) for c in str(2 * el))
Going further (too far?), this leads to:
def validate_credit_card_number(card_number):
temp_list = [int(c) for c in str(card_number)]
list1 = temp_list[-2::-2]
list2 = temp_list[::-2]
total_sum = sum(list2) + sum(sum(int(c) for c in str(2 * el)) for el in list1)
return total_sum % 10 == 0
Edit:
More simplification
We are using str
and int
to get the digits of a number... which is known to be smaller than 18 (2 * 9).
A trick could be, once again, to use divmod
returning the quotient and remainder and use sum on it.
total_sum = sum(list2)
for el in list1:
total_sum += sum(divmod(2 * el, 10))
or
total_sum = sum(list2) + sum(sum(divmod(2 * el, 10)) for el in list1)
Playing with indices
Instead of splitting the list into 2 lists, we could iterate over the (reversed) list once and handle differently elements at odd positions and elements at even positions.
We'd get something like:
def validate_credit_card_number(card_number):
temp_list = [int(c) for c in str(card_number)]
total_sum = 0
for i, e in enumerate(reversed(temp_list)):
if i % 2 == 0:
total_sum += e
else:
total_sum += sum(divmod(2 * e, 10))
return total_sum % 10 == 0
Or the more concise solution taking advantage of the fact that the divmod
trick works for both cases:
def validate_credit_card_number(card_number):
total_sum = 0
for i, c in enumerate(reversed(str(card_number))):
e = int(c) * (2 if i % 2 else 1)
total_sum += sum(divmod(e, 10))
return total_sum % 10 == 0
or
def validate_credit_card_number(card_number):
return sum(sum(divmod(int(c) * (2 if i % 2 else 1), 10))
for i, c in enumerate(reversed(str(card_number)))) % 10 == 0