Python Forum
Credit card number redacting script
Thread Rating:
  • 4 Vote(s) - 2.5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Credit card number redacting script
#1
Happy New Year Pythonistas!

I’m writing a Python shell script for fun. The purpose of the script is to ask the user for a fake credit card number (16 integers) and then returns a credit card number with the first 12 integer redacted with x’s.

I’m using a for loop with enumerate. I’m making a rookie mistake. I need some guidance.

Here is my script so far:

def ccredact(string):
   '''
   Take string input and then redact first 12 of 16 numbers
   '''
   spliced_list = list(string) # Convert string to list of numbers
  
   for index, value in enumerate(spliced_list):       
       # print(index, value) # test enumerate
       index[0:13] = value = 'x'
       result = spliced_list
       return result

string = str(input("Enter your fake credit card number: "))  # input something like 1234567812345678

ccredact(string)

if __name__ == "__main__":
   pass
In this script I define the function. In this function I first describe the functionality in a doc string. Then I splice the string of numbers into a list of numbers. Then I initiate the for loop using two variables (index and value) using enumerate. Then within the loop I try to re-assign the value of the first 12 integers to ‘x’ (this line is obviously my problem area, described below). After defining the function, I then name the string variable as I prompt the user to enter their fake 16 digit credit card number. Finally, I call the string.

Here is the output:
Error:
$ python script.py Enter your fake credit card number: 1234 Traceback (most recent call last): File "script.py", line 15, in <module> ccredact(string) File "script.py", line 9, in ccredact index[0:13] = value = 'x' TypeError: 'int' object does not support item assignment
The traceback is drawing my attention to line 9. I see two issues. The first is index and value are integers yet I am trying to assign an ‘x’ which is a string character. Though I’m not sure how to remedy this. The second issue at this line is that Python doesn’t support three variable assignments on a single line. I am not sure how to remedy this either. What would you people recommend? Any hints?
Reply
#2
As you say, index is an integer. You can't slice an integer, so index[0:13] is invalid. The only thing you have that you could do an assignment with a slice is spliced_list.

But I'm not sure how this is supposed to work. You're assigning one 'x' to a 13 long slice. I would think you would want to assign 12 x's to a 12 long slice. And your loop is just going to go through one iteration and then stop, because of the break statement. Are you planning to later put that into a conditional that finds the credit card number?
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#3
Thank you ichabod801 for your feedback so far.

(Jan-01-2019, 06:16 PM)ichabod801 Wrote: As you say, index is an integer. You can't slice an integer, so index[0:13] is invalid. The only thing you have that you could do an assignment with a slice is spliced_list.

index[0:13] is invalid as you pointed out. I’ve since swapped index[0:13] with spliced_list[0:13]. But I am still not getting to where I’d like to be. I’m still misunderstanding and misusing the enumerate function in my loop.

Quote:But I'm not sure how this is supposed to work. You're assigning one 'x' to a 13 long slice. I would think you would want to assign 12 x's to a 12 long slice.

Good point. To clarify, for the first 12 items in the list, I’m trying to convert each of them into ‘x’. I was trying to replace the first 12 spliced_list values to ‘x’. Once the replacement or exchange is complete, then I’d like to reassemble the list as a string by joining them back together. In the end my objective is take the input such as 1234 5678 1234 5678 and convert it so that it appears as xxxx xxxx xxxx 5678.

Quote:And your loop is just going to go through one iteration and then stop, because of the break statement.

I didn’t explicitly use break anywhere. I do use return. Does the return statement qualify as a break?

Quote:Are you planning to later put that into a conditional that finds the credit card number?

I am not intending to validate the true identity of the credit card number before, during or after.

After reading over your post the first time I can’t remember now what I picked up on, but I began wondering if it’s possible to achieve my desired result without enumerate or even without a loop at all. Just a simple casting call.

I looked up the replace method on Google. I found some docs on tutorialspoint and on Programiz. I played around with the method. Here is a fresh attempt with this new approach:

def ccredact(card_string):
    redacted_num = card_string.replace(card_string[0:12], 'xxxx xxxx xxxx ')
    print(redacted_num)

card_string = str(input("Enter your fake credit card number: "))  # User inputs something like 1234567812345678

ccredact(card_string)

if __name__ == "__main__":
    pass
Jambo fellow adventurers! I did it! Here is the output now:

Quote:$ python script_no_loop.py
Enter your fake credit card number: 1234567812345678
xxxx xxxx xxxx 5678

The quirk I can’t figure out now is at line 3. If I change line 3 to return redacted_num, when I run the script, after the user inputs their 16 digit number, nothing is returned. Why? I figure I am overlooking something really trivial. Would anyone care to comment on how I can properly return redacted_num?

Finally, I was also wondering if any of you would be able reproduce the desired output using a for loop with enumeration?
Reply
#4
That may work, but it's rather inefficient. I would use redacted_num = 'xxxx xxxx xxxx {}'.format(card_string[-4:]).

If you just change line 3 to return a value, you are not assigning the return value to anything on line 7. Change line 3 to return a value, and change lines 5 to 10 to:

if __name__ == '__main__':
    card_string = str(input("Enter your fake credit card number: "))
    redacted_num = ccredact(card_string)
    print(redacted_num)
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#5
Thank you for clarifying.

In order for a function to properly return a value, when the function is called, it’s necessary to declare it as a variable. I see now.

I suppose it is also better practices to execute the module’s code below the if __name__ == "__main__": conditional.

With these ideas in mind, here is my script now:

def ccredact(card_string):
    redacted_num = card_string.replace(card_string[0:12], '**** **** **** ')
    return redacted_num

if __name__ == "__main__":
    card_string = str(input("Enter your fake credit card number: "))
    result = ccredact(card_string)
    print(result)
Here is @ichabod801’s script:

def ccredact(card_string):
    redacted_num = 'xxxx xxxx xxxx {}'.format(card_string[-4:])
    return redacted_num

if __name__ == "__main__":   
    card_string = str(input("Enter your fake credit card number: "))  
    result = ccredact(card_string)
    print(result)
They both run beautifully without issue.

I understand that performing the initial credit card number redaction task is much more Pythonic and elegant to simply use format slicing. But I think it would be enormously helpful for me to learn the syntax of enumerate if a kind soul on this message board would be able to whip together a loop with this special function in the context of my particular use case here. I’ve made a valiant attempt in my original post here. I sincerely tried. And it was an epic fail.

I’ve got an over abundance of guides and tutorials for enumeration such as the one on Read the Docs, on Programiz and on SO. These help.

But seeing someone demonstrate enumerate in the context of my fake credit card redaction task here I think would really help me understand the concept of enumeration better.

Thanks again, @ichabod801.
Reply
#6
(Jan-15-2019, 02:40 AM)Drone4four Wrote: In order for a function to properly return value, when the function is called, it’s necessary to declare it as a variable. I see now.

Actually, defining a variable is not necessary, the return statement is necessary. This works just fine:

def three():
     return 5
(Jan-15-2019, 02:40 AM)Drone4four Wrote: But seeing someone demonstrate enumerate in the context of my fake credit card redaction task here I think would really help me understand the concept better.

def ccredact(card_string):
    redacted = []
    for letter_index, letter in enumerate(card_string):
        if letter_index < 12:
            redacted.append('*')
        else:
            redacted.append(letter)
        if letter_index in (3, 7, 11):
            redacted.append('-')
    return ''.join(redacted)
Craig "Ichabod" O'Brien - xenomind.com
I wish you happiness.
Recommended Tutorials: BBCode, functions, classes, text adventures
Reply
#7
Thank you @ichabod801: This is enormously instructive!
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Having strange results from an RFID HID card reader - I'm stuck orbisnz 1 1,491 Mar-28-2022, 08:20 AM
Last Post: Larz60+
  SQL wild card use hammer 3 1,275 Jan-07-2022, 02:17 PM
Last Post: hammer
  Validating credit card frequency 8 4,192 Nov-05-2018, 07:36 PM
Last Post: frequency
  individual's ID card number in python danpek 2 3,775 Jun-14-2018, 04:07 PM
Last Post: DeaD_EyE
  Prime number Script Problem Codezters 4 4,059 Jul-21-2017, 03:07 AM
Last Post: Larz60+
  RAW IO disk access for SD Card shift838 1 8,585 Feb-27-2017, 03:35 AM
Last Post: Larz60+
  "Card Dealing" Python code 2.7 Paradoxalis 3 6,612 Nov-17-2016, 11:32 PM
Last Post: Larz60+

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020