I'm having issues looping through a tuple - python

I'm writing a program to simulate a bank scenario. It allows users (once they have logged in) to display their balance, withdraw and deposit money, and will eventually contain a transfer subroutine.
My problem is the login subroutine. I am having trouble getting my code to loop through both accounts in the tuple that saves them. I can log in to the first account and perform all the code's functions with that account but when I try to access the second account, I get the error message telling me that the account is not found. I am very certain that this is to do with the way I have written my loop but I cannot figure out how to fix it.
If someone could provide me with a solution that I can use to fix this error then I would be very grateful. If it is possible, could the solution also stick to the general format of my code and not be one where I have to create new functions and change the entire body of my LogOn function?
Here is the code:
accounts=[
["Luke",'00001','1234',1337],
["Louis",'00002','4321',420],
]
name = ""
x = []
def LogOn():
global name
global x
print ("Welcome to the Bank of Bailey")
accnum = raw_input("-please enter your account number-")
if len(accnum) != 5:
print("That number is not valid, please enter a valid number")
LogOn()
else:
for x in range(0,len(accounts)):
if accnum in accounts[x][1]:
name = accounts[x][0]
print ("Account found")
pin = raw_input("-please enter your pin-")
if pin in accounts[x]:
print ("Pin found")
MainMenu()
else:
print("Pin not found")
print("Please try again")
LogOn()
break
else:
print("Account not found")
print("Please try again")
LogOn()

The problem is:
if accnum in accounts[x][1]:
do something
else:
print("Account not found")
print("Please try again")
LogOn()
The else branch is called already for the first iteration (with x = 0)!
You should check after the while-loop if an account was found and otherwise print your error message.
Instead of recursing to LogOn() you could better use an outer (endless-)while-loop which calls LogOn(). In LogOn then just do a return in error case.

A few observations:
The else branch is called on the first iteration when x=0. You need to finish the for loop, then branch if the account has not been found.
if pin in accounts[x] is a security hole because it allows people to use their name or account number as their pin!
"if accnum in account[1]" should be "if accnum == account[1]"
Hope that helps!
accounts=[
["Luke",'00001','1234',1337],
["Louis",'00002','4321',420],
]
name = ""
x = []
def LogOn():
global name
global x
print ("Welcome to the Bank of Bailey")
accnum = raw_input("-please enter your account number-")
if len(accnum) != 5:
print("That number is not valid, please enter a valid number")
LogOn()
else:
found = False
for account in accounts:
if accnum == account[1]:
found = True
name = account[0]
print ("Account found")
pin = raw_input("-please enter your pin-")
if pin in account[2]:
print ("Pin found")
MainMenu()
else:
print("Pin not found")
print("Please try again")
LogOn()
break
if not found:
print("Account not found")
print("Please try again")
LogOn()

I like #TimSC's answer, but I'll add a few more observations:
LogOn is not a good function name; it should be log_on to match PEP8 naming conventions (LogOn suggests it is a class rather than a function).
You declare name and x as globals, then never use them. Globals are almost always a bad idea, and unused declarations just clutter your code.
As #CTX pointed out, you are using recursion (LogOn calling LogOn calling MainMenu calling LogOn etc) where iteration would make a lot more sense - you should back out on an error, not call again a level deeper.
Mixing input/output code and action code is usually a bad decision; it makes your code less flexible and reusable and often harder to debug as well. You should instead have an input/output function which calls an action function with the values it needs.
Giving separate error messages for bad account number, bad pin number is a security hole; it makes it much easier for would-be attackers to find valid account numbers to attack.
Looking things up in a list is relatively slow (O(n) ie time proportional to the number of items in the list). Use a dict instead (O(1) ie constant time - a quick hash calculation takes you right to the item you are looking for).
Here is a fancied-up version; may it give you things to think about:
import sys
# version compatibility shim
if sys.hexversion < 0x3000000:
inp = raw_input # Python 2.x
else:
inp = input # Python 3.x
class Account:
index = {}
__slots__ = ("accnum", "pin", "name", "balance")
#classmethod
def log_in(cls, accnum, pin):
key = (accnum, pin)
acc = Account.index.get(key, None)
if acc is None:
raise ValueError("No such login (bad account# or PIN)")
else:
return acc
def __init__(self, accnum, pin, name, balance):
# save values
self.accnum = accnum
self.pin = pin
self.name = name
self.balance = balance
# add self to account index
key = (accnum, pin)
Account.index[key] = self
# create accounts
Account('00001', '1234', "Luke", 1337)
Account('00002', '4321', "Louis", 420)
def main():
while True:
print("\nWelcome to the Bank of Bailey")
accnum = inp("Please enter your account number: ").strip()
pin = inp("Please enter your PIN: ").strip()
try:
acc = Account.log_in(accnum, pin)
print("\nYour account has a current balance of ${:0.2f}".format(acc.balance))
print("Thank you for banking with us!")
except ValueError as ve:
print(ve)
if __name__ == "__main__":
main()

Related

Importing class from module but functions not working

I created two programs, one is an ATM simulator and other is a Virtual Doctor which reads data from excel sheet and on the basis of user input, tells what disease the user might be suffering from.
now I want to connect the atm to virtual doctor such that it withdraws the amount of medicines from the bank account
I imported atm to virtual doctor but the functions don't seem to be working, they do nothing when called and the process exits.
#code for ATM
userpin = ["1234", "2345", "3456", "4567"]
userpass = ["1234", "2345", "3456", "4567"]
username = ["Rishabh", "Siddharth", "Kashish", "Mahima"]
userbalance = [20500, 43567, 45672, 67800]
class Bank:
def __init__(self, bal=0, index=0):
#if __name__ == '__main__':
self.bal = bal
self.index = index
def start(self):
if __name__ == '__main__':
print("\t\t=== Welcome to ATM ===")
inputpin=input("Enter your pin :")
inputpass= input("Enter your password :")
b1.pinpasscheck(inputpin,inputpass)
def pinpasscheck(self,pin,passw):
self.pin=pin
self.passw=passw
inputpin=pin
inputpass=passw
index=0
flag= False
for i in range(0,len(userpin)):
if inputpin==userpin[i]:
index=i
print(index)
if inputpass==userpass[index]:
print("Login Succeeded !")
flag= True
b1.operationlist(index)
if flag==False:
print("Login invalid. Please check username or password")
else:
pass
else:
pass
def operationlist(self,indexval):
self.indexval=indexval
index=indexval
print("\n Hello, ", username[index])
print("""
1) Balance
2) Withdraw
3) Deposit
4) Change password
5) Quit
""")
useroption = int(input("Select an option:"))
if useroption == 1:
print("\nYour current balance is {}".format(userbalance[index]))
b1.operationlist(index)
elif useroption == 2:
amount= int(input("\nEnter amount you want you want to withdraw : Rs"))
b1.withdraw(amount,index)
else:
print("None of the above options selected. Please select any one of the provided options.")
b1.operationlist(index)
def withdraw(self, amt, index):
self.amt= amt
amount = amt
self.index= index
if amount > userbalance[index]:
print("Oops! Insufficient funds.")
b1.operationlist(index)
rembalance = userbalance[index] - amount
userbalance.remove(userbalance[index])
userbalance.insert(index, rembalance)
print("Your remaining balance is: ", userbalance[index])
b1.operationlist(index)
b1 = Bank()
b1.start()
#code for VirtualDoctor
import xlrd
import pandas
from NewATM import Bank
path = "symptoms.xlsx"
book = xlrd.open_workbook(path)
sheet= book.sheet_by_index(0)
b2= Bank()
data= [[sheet.cell_value(r,c) for c in range (sheet.ncols)] for r in range (sheet.nrows)]
diseaselist= []
for i in range (1,sheet.nrows):
diseaselist.append(sheet.cell_value(i,0))
symptoms=[]
for i in range (1, data.__len__()):
tmp= data[i][1:5]
symptoms.append(tmp)
print(symptoms)
inputlist = []
b2.start() #THIS DOES NOT WORKK !!!!!!!!!!!!!!!!!!!!! WHY ???
The virtual doctor program should now go to the atm and then I can move forward with my code, but this doesn't seem to be working.
Your problem is that the code from your ATM class requires data which is set as global variables in the class itself. In terms of OOP, this is bad practice and in your particular case the code just won’t work as the variables are out of scope to the main (virtual doctor).
Either move those variables into the class itself (which is still bad practice) or keep them elsewhere and import the matrix of data when calling the class, and pass it to each individual functions.
Lastly you are creating an instance of bank called b1 to use in the class, which doesn’t make sense in terms of OOP. Change the b1 to self such that your calling the functions like self.function (one edit is changing b1.pinpasscheck() to self.pinpasscheck()).

Python - Access and use specific object during runtime

I am working on a bank account program where the user logs in with a four digit number (pin).
I want to find a way to access a specific object with all its attributes at runtime after the right pin has been entered.
class Konto(object):
def __init__(self, account_holder, balance , pin):
self.account_holder = account_holder
self.balance = balance
self.pin = pin
I have three different objects defined in a list
kontoList = []
kontoList.append(Konto("Person1", 143541, 1223)),
kontoList.append(Konto("Person2", 6230, 1234)),
kontoList.append(Konto("Person3", 4578, 4321))
The last attribute is the Pin that is entered by the user. When the program checks the pin is '1234' for example it displays a menu where you can get the current balance, account holder etc. In this case it would be 6230 (balance) and Person2 (account holder). So here is some code:
pin = input("PIN: ")
for konto in kontoList:
if konto.pin == pin:
print("Valid PIN")
continue
else:
print("not valid")
break
while True:
print("1: Withdrawal \n"
"2: Deposit \n"
"3: Transfer \n"
"4: Current Balance \n"
"5: Account Holder \n"
"6: Quit \n")`
choice = input("Your Choice: ")
Is there any way to access the specific object during runtime and then go on to work with it? I've looked up getattr() but it does not seem useful in this situation.
You could simply create a list of pins, then check whether the pin you're checking is contained in that list:
kontoPinList = [konto.pin for konto in kontoList]
and then you would check whether your pin is in the kontoPinList with:
pin in kontoPinList
EDIT: If you want to keep working with the konto you do the following:
for konto in kontoList:
if konto.pin == pin:
#do something with the konto here
EDIT nr.2: If you wish to now call functions on the konto, such as account_holder(), you just do account_holder(konto) and that should work.
The reason my first response was to write a getPin() function is because while this will not solve your problem, it is a good idea to "protect" your variables by deciding how you want to return them. (it's more of a java thing than a python thing)
However, as you pointed out, it is a useless function if all you're interested in is simply returning konto.pin .
You could try something like this:
kontos_with_pin = filter((lambda k: k.pin == pin), kontoList)
if len(kontos_with_pin) == 1:
relevant_konto = kontos_with_pin[0]
# Do something with relevant_konto
else:
# handle case where there is no konto with that pin, or more than one, etc.

Bank ATM Program login

I want to make this program that acts as a bank, how do I make sure the correct ID number must be entered with the correct pin and have it depending on the id you entered print hello then their name and prompt how much money they have in the bank.
attempts = 0
store_id = [1057, 2736, 4659, 5691, 1234, 4321]
store_name = ["Jeremy Clarkson", "Suzanne Perry", "Vicki Butler-Henderson", "Jason Plato"]
store_balance = [172.16, 15.62, 23.91, 62.17, 131.90, 231.58]
store_pin = [1057, 2736, 4659, 5691]
start = int(input("Are you a member of the Northern Frock Bank?\n1. Yes\n2. No\n"))
if start == 1:
idguess = ""
pinguess = ""
while (idguess not in store_id) or (pinguess not in store_pin):
idguess = int(input("ID Number: "))
pinguess = int(input("PIN Number: "))
if (idguess not in store_id) or (pinguess not in store_pin):
print("Invalid Login")
attempts = attempts + 1
if attempts == 3:
print("This ATM has been blocked for too many failed attempts.")
break
elif start == 2:
name = str(input("What is your full name?: "))
pin = str(input("Please choose a 4 digit pin number for your bank account: "))
digits = len(pin)
balance = 100
while digits != 4:
print("That Pin is Invalid")
pin = str(input("Please choose a 4 digit pin number for your bank account: "))
digits = len(pin)
store_name.append(name)
store_pin.append(pin)
I'm very impressed by how much you've elaborated on your program. Here's how I would view your solution.
So to create a login simulation, I would instead use a dictionary. That way you can assign an ID to a PIN. For example:
credentials = {
"403703": "121",
"3900": "333",
"39022": "900"
}
Where your ID is on the left side of the colon and the PIN is on the right. You would also have to assign the ID to a name that belongs to that ID using, you guessed it, a dictionary!
bankIDs = {
"403703": "Anna",
"3900": "Jacob",
"39022": "Kendrick"
}
Now that you've done that, you can create your virtual login system using if/else control flow. I've made my code like this:
attempts = 0
try:
while attempts < 3:
id_num = raw_input("Enter your ID: ")
PIN = raw_input("Password: ")
if (id_num in credentials) and (PIN == credentials[id_num]):
print "login success."
login(id_num)
else:
print "Login fail. try again."
attempts += 1
if attempts == 3:
print "You have reached the maximum amount of tries."
except KeyboardInterrupt:
print "Now closing. Goodbye!"
Note the try and except block is really optional. You could use the break operator like you did in your code if you wanted to, instead. I just like to put a little customization in there (Remember to break out of your program is CTRL-C).
Finally, Python has a way of making life easier for people by using functions. Notice I used one where I put login(id_num). Above this while loop you'll want to define your login so that you can display a greeting message for that particular person. Here's what I did:
def login(loginid):
print "Hello, %s!" % bankIDs[loginid]
Simple use of string formatting. And there you have it. The same can be done with displaying that person's balance. Just make the dictionary for it, then print the code in your login definition.
The rest of the code is good as it is. Just make sure you've indented properly your while-loop inside the elif on the bottom of your code, and your last 2 lines as well.
Hope I helped. Cheers!

Python - input control

I have problem with users input control in one function in Python 3.4.
def input_name (*args):
name_output = ""
name_input = input ("Give first name: ")
if name_input.isalpha() and name_input[0].isupper() == True:
name_output += name_input
return (name_output)
else:
print ("Wrong, do it again")
input_name ()
name = input_name()
print(name.lower())
I am trying to catch users wrong input - so the name must be alphabetical and first letter must be uppercase. In future code I will create users login name with lowercase letters, so I am trying to print users name with small leters for login name. And there is problem.
When I type name firs time well, it's ok
When I type first time name with 1 lowercase letter (name) and then I write it correctly (Name), it tells me Error, I don't understand why. Can you tell me, what is my mistake?
Thank you very much for showing the path.
Mirek
The error is caused by the last line. Since your input is wrong the first time, the function returns None, so name.lower() raises an exception. I wouldn't use recursion in this case.
def input_name():
while True:
name_input = input ("Give first name: ")
if name_input.isalpha() and name_input[0].isupper():
return name_input
else:
print ("Wrong, do it again")
name = input_name()
print(name.lower())
Hope it helps!

Python Amateur - 'Greeting program' - 'Referenced before assignment error'

Like I said in my previous question, I'm a python amateur. I've made a couple silly mistakes. I'm attempting to make a highly simple greeting program using Python 3.4 however I have encountered an error. The error I have is:
UnboundLocalError: local variable 'lastNameFunction' referenced before assignment
Here's my code (I know I probably don't need to post it all, but there isn't much of it):
def main():
import time
running = True
while (running):
firstNameInput = input("What is your first name?\n")
firstName = firstNameInput.title()
print ("You have entered '%s' as your first name. Is this correct?"%firstName)
time.sleep (1)
choice = input("Enter 'Y' for Yes or 'N' for No\n")
if(choice.upper() == "Y"):
lastNameFunction()
elif(choice.upper() == "N"):
main()
def lastNameFunction():
lastNameInput = input("Hi %s. Please enter your last name. \n"%firstName)
lastName = lastNameInput.title()
if __name__ == '__main__':
main()
I'd appreciate any help and advice! Please take into consideration I am really new to this stuff. I'm also not quite sure on having a function inside of a function, but I thought it would be a fix so that the 'firstName' was available when entering the 'lastName'.
Thanks in advance! :)
You need to move the lastNameFunction declaration somewhere before you call it using lastNameFunction(), e.g.:
def main():
import time
running = True
while (running):
firstNameInput = input("What is your first name?\n")
firstName = firstNameInput.title()
print ("You have entered '%s' as your first name. Is this correct?" % firstName)
time.sleep (1)
choice = input("Enter 'Y' for Yes or 'N' for No\n")
def lastNameFunction():
lastNameInput = input("Hi %s. Please enter your last name. \n" % firstName)
lastName = lastNameInput.title()
if(choice.upper() == "Y"):
lastNameFunction()
elif(choice.upper() == "N"):
main()
if __name__ == '__main__':
main()
You can also move it outside the main function, but you will then need to pass the firstName in using the function arguments:
def lastNameFunction(firstName):
lastNameInput = input("Hi %s. Please enter your last name. \n" % firstName)
lastName = lastNameInput.title()
def main():
...
lastNameFunction(firstName)
...
Move your lastNameFunction to somewhere before the call. Ideally, you would place this above the main function.
def lastNameFunction():
lastNameInput = input("Hi %s. Please enter your last name. \n"%firstName)
lastName = lastNameInput.title()
def main():
...
The problem is you called lastNameFunction() before you defined it in the while loop. Try defining the function outside the while loop.
The organisation of the whole program seems a little bit off.
Imports usually go at the top of the module.
Functions defined in functions are usually just for closures, which you a) don't need here and b) might be a bit advanced for your current experience level.
Recursive calls like your calling main() from within main() are wrong. When you adopt that style of control flow instead of a loop, you will eventually run into limitations of recursive calls (→ RuntimeError). You already have the necessary loop, so simply leaving out the elif branch already asks the user again for the first name.
running isn't used anywhere, so you can remove it and just use while True:.
I would move asking the user for ”anything” + the question if the input was correct into its own function:
def ask_string(prompt):
while True:
result = input(prompt).title()
print("You have entered '{0}'. Is this correct?".format(result))
choice = input("Enter 'Y' for Yes or 'N' for No\n")
if choice.upper() == 'Y':
return result
def main():
first_name = ask_string('What is your first name?\n')
last_name = ask_string(
'Hi {0}. Please enter your last name.\n'.format(first_name)
)
print(first_name, last_name)
if __name__ == '__main__':
main()

Categories