Unable to access what should be a global variable from function - python

from better_blackjack.preset_folder import build_deck
"""
This file is used to load all the things we need to run game.py (player_chips, deck, number of hands, bet amount)
"""
with open(r"C:\Hacks\python\programs\better_blackjack\Save_settings/chips.txt", "r") as file:
file_data = file.read()
if len(file_data) == 0:
player_chips = 1000000 # This will get altered (live amount of chips)
else:
try:
player_chips = int(file_data)
except ValueError:
print('File corrupted... restarting player chips ')
player_chips = 1000000
print(player_chips)
def get_num_hands():
try:
num_hands = (int(input('How many hands would you like ? ')))
get_bet_amount(num_hands)
except ValueError:
print('Use a valid number.')
get_num_hands()
def get_bet_amount(num_hands):
list_of_bets = []
for i in range(1, num_hands+1):
try:
print('How much would you like to bet on hand', i, ' Balance', player_chips)
bet_amount = int(input())
list_of_bets.append(bet_amount)
player_chips = player_chips - bet_amount
if player_chips < 0:
print('Bets exceed player balance... Restarting betting process')
player_chips = int(file_data)
get_bet_amount(num_hands)
return None # ends func
except ValueError:
print('Please use numbers only !... Restarting betting process')
player_chips = int(file_data)
get_bet_amount(num_hands)
return None # ends func
deck = build_deck.deck
get_num_hands()
I am getting the error 'player_chips' referenced before assignment on the following line - print('How much would you like to bet on hand', i, ' Balance', player_chips)
However it is defined before we call any of our functions how could this be ?

If a variable is modified inside a function, it is a local variable, and has nothing to do with the global variable of the same name. Hence player_chips inside get_bet_amount has nothing to do with the variable of the same name used at top level.
Add global player_chips inside your function to override this behavior.

Related

How can I unit test a function that is called by another function

I am trying to unit test my program and have decided to unit test the withdraw_cash function. However, it is called by the bank_atm function. I have never called a function that is dependent on another function and am confused about how to do this. Would i use mock and patch to do this?
The tests would be:
check whether entering valid amount
check whether amount is less than balance
user = {
'pin': 1234,
'balance': 100
}
def withdraw_cash():
while True:
try:
amount = int(input("Enter the amount of money you want to withdraw: "))
except ValueError as v:
print(f"Enter correct amount: ")
else:
if amount > user['balance']:
raise ValueError("You don't have sufficient balance to make this withdrawal")
else:
user['balance'] = user['balance'] - amount
print(f"£{user['balance']}")
return
finally:
print("Program executed")
def bank_atm():
count = 0
to_exit = False
while (count < 3) and (not to_exit):
try:
pin = int(input('Please enter your four digit pin: '))
except ValueError:
print("Please enter correct pin")
count += 1
if pin != user['pin']:
print("Pin does not match.. Try Again")
count += 1
else:
withdraw_cash()
to_exit = True
if count == 3:
print('3 UNSUCCESFUL PIN ATTEMPTS, EXITING')
print('!!!!!YOUR CARD HAS BEEN LOCKED!!!!!')
try:
bank_atm()
except ValueError as v:
print(f"ERROR: {v}")
To expand on the comments. Put UI interactions in separate functions, such as getpin and getpounds. They can then be tested separately from the business functions. One can either mix manual input with the unittests or automate them by patching sys.stdin/out. My proof-of-concept experiment, which passes on 3.11.0b3 run from IDLE editor.
import sys
import unittest
from unittest.mock import patch
def getpin(realpin):
return input('Your pin? ') == realpin
class ManualTest(unittest.TestCase):
def test_pin(self):
print('\n(Enter 1234)')
self.assertEqual(getpin('1234'), True)
print('\n(Enter 1)')
self.assertEqual(getpin('1234'), False)
class MockOut:
def write(self, string): pass
# Or 'write = Mock()' to save and check prompts, using mockout.
class MockIn:
def readline(self): # input() uses readline, not read.
return self.line
#patch('sys.stdin', new_callable=MockIn)
#patch('sys.stdout', new_callable=MockOut)
class AutoTest(unittest.TestCase):
def test_pin(self, mockout, mockin):
mockin.line='1234\n' # input removes '\n' if present.
self.assertEqual(getpin('1234'), True)
mockin.line='1233'
self.assertEqual(getpin('1234'), False)
if __name__ == '__main__':
unittest.main(verbosity=2)
I have now refactored my program, by breaking it down into separate functions as you said but when I try and run a test specifically for one function, it runs the whole thing and asks for input from each function. I feel like testing each function individually will have to require specific parameters not dependent on the other function.
user = {
'pin': 1234
}
def withdraw_cash(amount):
balance_account = 100
if amount > balance_account:
raise ValueError("You don't have sufficient balance to make this withdrawal")
else:
new_balance = balance_account - amount
return new_balance
def get_pin():
count = 0
to_exit = False
while (count < 3) and (not to_exit):
try:
pin = int(input('Please enter your four digit pin: '))
except ValueError:
print("Please enter correct pin")
count += 1
if pin != user['pin']:
print("Pin does not match.. Try Again")
count += 1
else:
return get_amount(pin)
#return pin
if count == 3:
a = '3 UNSUCCESFUL PIN ATTEMPTS, EXITING \n !!!!!YOUR CARD HAS BEEN LOCKED!!!!!'
return a
def get_amount(pin):
while True:
try:
amount = int(input("Enter the amount of money you want to withdraw: "))
except ValueError as v:
print(f"Enter correct amount: ")
else:
#print(amount)
return withdraw_cash(amount)
#return amount
try:
get_pin()
except ValueError as v:
print(f"ERROR: {v}")

How write User Input Data to External Text File?

I would like to be able to take the test scores the user inputs and write to an external text file. Then have the application read off the values from the and calculate the average. However, I am unsure as to how to implement the python syntax within the loop and the functions. I've attempted to utilize my resources to get a better idea of how to do this, but I've been having some trouble understanding how python handles external files. In addition, would using append be better than write in this scenario?
Current Syntax:
def testAvgCalculation():
#Variables
total = 0
total_quiz = 0
while True:
#User Input and Variable to stop loop
inpt = input("Enter score: ")
if inpt.lower()== 'stop':
break
#Data Validation
try:
if int(inpt) in range(1,101):
total += int(inpt)
total_quiz += 1
else:
print("Score too small or Big")
except ValueError:
print("Not a Number")
return total, total_quiz
def displayAverage(total, total_quiz):
average = total / total_quiz
print('The Average score is: ', format(average, '.2f'))
print('You have entered', total_quiz, 'scores')
#Main Function
def main():
total, total_quiz = testAvgCalculation()
displayAverage(total, total_quiz)
#Run Main Function
main()
This is hacky as heck, but I tried to work with what was already there. I split the data validation section of the original function off into a separate function. In main() it returns its value counter, which keeps track of how many values were entered, to calculate_average(), which then reads the file line by line until counter becomes 0, which means it's about to read the word "stop" (which allows EOF recognition via the 'and' in the if statement), performs the calculation and returns its values.
def write_file():
#Variables
counter = 0
file = open("Scores.txt", "w")
while True:
#User Input and Variable to stop loop
inpt = input("Enter score: ")
file.write(inpt + "\n")
if inpt.lower()== 'stop':
file.close()
break
counter += 1
return counter
def calculate_average(counter):
total = 0
total_quiz = counter
scores = open("Scores.txt", "r")
s = ""
try:
while counter > 0 and s != 'stop':
s = int(scores.readline())
if int(s) in range(1,101):
total += int(s)
counter -= 1
else:
print("Invalid data in file.")
except ValueError:
print("Invalid data found")
return total, total_quiz
def displayAverage(total, total_quiz):
average = total / total_quiz
print('The Average score is: ', format(average, '.2f'))
print('You have entered', total_quiz, 'scores')
#Main Function
def main():
total, total_quiz = calculate_average(write_file())
displayAverage(total, total_quiz)
#Run Main Function
main()
NOTE: the file is created initially in write mode which overwrites the file each time so you never need a new one. if you want to keep a record you might like to change it to append, though you'll need to manage extracting the proper lines from among old input.
Not pretty at all, but should give you an idea of how to accomplish what you were going for.

Python variables and function not defined

in order to fix the NameError (name not defined problem) I used the global keyword like this, but I don't think it's a good solution. And I want to put the last part of the code into a function (main()), but then again the not defined error occurs with purchase_item function, Can someone help me improve this code?
from RetailItem import RetailItem
class CashRegister:
global Item_List
global Cashier_List
global total
Item_List = [ RetailItem("Item 1","Jacket", 12, 59.95),
RetailItem("Item 2", "Designer Jeans", 40, 34.95),
RetailItem("Item 3", "Shirt", 20, 24.95) ]
Cashier_List = []
total = 0
def purchase_item(RetailItem):
global Cashier_List
Cashier_List.append(RetailItem)
def get_total():
global Cashier_List
global total
for o in Cashier_List:
total += o.getPrice()
return total
def show_items():
global Cashier_List
for o in Cashier_List:
print(o)
def clear():
global Cashier_List
del Cashier_List[:]
print("Here are available items:")
for o in Item_List:
print(o)
while True:
x = input("Select items by number to buy or enter \"n\" to finish shopping: ")
if x != "n":
try:
purchase_item(Item_List[int(x)-1])
print("ADDED!")
except ValueError:
print("Invalid number, try again!")
except IndexError:
print("Invalid number, try again!")
else:
break
print("......The total price is ${:0.2f}\n......This is your checkout items:".format(get_total()))
show_items()

Need help simplifying my script in Python

I am new to programming Python, learning mostly through "learn python the hard way." I recently wrote a script that will help me calculate the overtime earnings for my piece work employees. The script works, but it just doesn't look right to me. I would love input on how I could have saved myself some steps. I know using globals is taboo, but I could not figure out another way to get the answers outside those functions. Thank you!!
# THIS SCRIPT WILL CALCULATE THE NUMBER OF PAID HOURS, OVERTIME HOURS, PIECE RATE HOURS AND OVERTIME HOURS OWED TO AN EMPLOYEE.
number_of_straight_hours_worked = False
number_of_overtime_hours_worked = False
employee_travel_time = False
days_worked = False
employee_earnings = False
employee_hourly_rate = False
employee_overtime_rate = False
#Function to determine number of straight hours worked.
def straight_hours():
global number_of_straight_hours_worked
number_of_straight_hours_worked = float(number_of_straight_hours_worked)
while number_of_straight_hours_worked == False:
number_of_straight_hours_worked = raw_input("How many straight hours did the employee work? ")
try:
int(number_of_straight_hours_worked)
except ValueError:
print("Must use a number")
number_of_straight_hours_worked = False
else:
print ("Thank you.")
#print number_of_straight_hours_worked
# Determines number of overtime hours.
def overtime_hours():
global number_of_overtime_hours_worked
number_of_overtime_hours_worked = float(number_of_overtime_hours_worked)
while number_of_overtime_hours_worked == False:
number_of_overtime_hours_worked = raw_input("How many overtime hours did the employee work? ")
try:
int(number_of_overtime_hours_worked)
except ValueError:
print("Must use a number")
number_of_overtime_hours_worked = False
else:
print ("Thank you.")
#print number_of_overtime_hours_worked
#Calcualtes the employee's overtime rate.
def employee_ot_calculation():
global employee_hourly_rate
global employee_overtime_rate
while employee_hourly_rate == False:
employee_hourly_rate = raw_input("What is the employee's hourly rate? ")
try:
float(employee_hourly_rate)
except ValueError:
print("Must use a number.")
#print employee_hourly_rate
else:
employee_overtime_rate = float(employee_hourly_rate) * 1.5
#Stores travel time hours
def travel_time():
global employee_travel_time
while employee_travel_time == False:
employee_travel_time = raw_input("How many hours of travel? ")
try:
int(employee_travel_time)
except ValueError:
print("Must use a number.")
employee_travel_time = False
else:
print ("Thank you.")
#print employee_travel_time
#Stores number of working days. Not used in version .001
def number_of_days_worked():
global days_worked
while days_worked == False:
days_worked = raw_input("How many days did the employee work? ")
days_worked = float(days_worked)
try:
int(days_worked)
except ValueError:
print("Must use a number.")
days_worked = False
else:
print ("Thank you.")
#Stores earnings made by piece work from employee.
def employee_piece_earnings():
global employee_earnings
while employee_earnings == False:
employee_earnings = raw_input("What did the employee earn through piece rate (format: 0.00)? ")
employee_earnings = float(employee_earnings)
try:
float(employee_earnings)
except ValueError:
print("Must use a number, no dollar sign.")
employee_earnings = False
else:
print ("Thank you.")
#print employee_earnings
# Calculates what the employee will earn this pay period between hours and piece work.
def overtime_piece():
total_hours_worked = float(number_of_straight_hours_worked) + float(number_of_overtime_hours_worked)
# print total_hours_worked
actual_working_hours = float(total_hours_worked) - float(employee_travel_time)
#print actual_working_hours
piece_overtime = float(actual_working_hours) - 40
#print piece_overtime
overtime_rate = float(employee_earnings / actual_working_hours)
#print overtime_rate
earned_straight_pay = float(number_of_straight_hours_worked) * float(employee_hourly_rate)
print "This employee earned $%.2f in straight pay: %.2f hours at $%.2f per hour" % (earned_straight_pay, number_of_straight_hours_worked, employee_hourly_rate)
earned_hourly_overtime = (float(total_hours_worked) - float(actual_working_hours)) * float(employee_overtime_rate)
print "This employee earned $%.2f in hourly overtime: %.2f hours at $%.2f per hour" % (earned_hourly_overtime, number_of_overtime_hours_worked, employee_overtime_rate)
earned_piece_overtime = float(overtime_rate) * float(piece_overtime)
print "This employee earned $%.2f in piece OT: %2f for each of working hour of the %.2f hours of overtime" % (earned_piece_overtime, overtime_rate, piece_overtime)
total_employee_earnings = float(earned_straight_pay) + float(earned_hourly_overtime) + float(earned_piece_overtime) + float(employee_earnings)
print "This employee earned a total of $%.2f this pay period." % total_employee_earnings
employee_ot_calculation()
straight_hours()
overtime_hours()
travel_time()
employee_piece_earnings()
overtime_piece()
To avoid using global variables, what you want to do is use function arguments and return values.
Function arguments
Let's take your first function as an example.
You can define a variable outside of the scope of your function and pass it as an argument between the two parenthesis. You can then manipulate your variable at your convenience inside of your function. You can pass as many arguments as you want.
number_of_straight_hours_worked = 1 # Define a variable outside of function
def straight_hours(number_of_straight_hours_worked): #Pass as argument
number_of_straight_hours_worked += 1 #Do whatever you want
Returning Values
The function straight_hours takes the input on how many straight hours an employee has worked. Instead of using a global variable, what you want to do is to return the value.
number_of_straight_hours_worked = "" # Define a variable outside of function
def straight_hours(number_of_straight_hours_worked): #Pass as argument
number_of_straight_hours_worked = input("How many hours?")
return number_of_straight_hours_worked
#Then retreive the value by doing
number_of_hours = straight_hours(number_of_straight_hours_worked)
As you can see, the line number_of_hours = straight_hours() calls the functions and affects the return the return value to the number_of_hours variable.
Other notes
I would also advise shrinking and simplifying you variable names.
Also, when you declare a variable like you do at the top of your code, I would advise either declare them as NONE or not do it at all. Declaring them to False which is of type boolean to then convert it to a float makes no sense to me.
Separete your validation code from your actual calculations. This way, you can test your calculations separately.

Appending to a file, then reading from it into a list, then re-appending to it and overwriting certain parts

I want to be able to have a program whereby the user can input a paragraph/sentence/word/character whatever and have that stored in a list e.g. in list[0]. Then I want them to be able to write another bit of text and have that stored in e.g. list[1]. Then at any time I want the user to be able to read that from the list by choosing which segment they want to read from e.g. reading "hello" from list[0] whilst in list[1] "hi" is stored. Then when the user exits the program I want the list to be written to an external file. Then, at next start up, the program should read the file contents and store it again in the list so that the user can add more bits of text or read the current bits. When the list is saved to a file it should append new or changed parts but overwrite parts that are the same so as not to have duplicates. I have attempted this without much success. I am to be honest not sure if it is possible. I have browsed similar forums and have found that hasn't helped much so here it is.
My code so far:
import os
import time
import csv
global write_list
global f1_contents
write_list = []
def write():
os.system("cls")
user_story = input("Enter your text: \n")
write_list.append(user_story)
def read():
os.system("cls")
user_select_needs = True
while user_select_needs == True:
user_select = input("Enter the list section to read from or type exit: \n")
if user_select == "exit":
user_select_needs = False
try:
int(user_select)
select = user_select
select = int(select)
try:
print(write_list[select])
user_select_needs = False
enter = input("Press enter:")
except:
print("There is not stored data on that section!")
except ValueError:
print("That is not a valid section!")
def exit():
os.system("cls")
max_num_needs = True
while max_num_needs == True:
set_max_num = input("Set the storage: \n")
try:
int(set_max_num)
max_num = set_max_num
max_num = int(max_num)
max_num_needs = False
except:
print("It must be an integer!")
for i in range(0, max_num):
f = open("Saves.txt", "a")
f.write(write_list[i])
f.close()
os._exit(1)
def main():
store_num_needs = True
while store_num_needs == True:
set_store_num = input("State the current storage amount: \n")
try:
int(set_store_num)
store_num = set_store_num
store_num = int(store_num)
store_num_needs = False
except:
print("It must be an integer!")
try:
f1 = open("Saves.txt", "r")
for i in range(0, store_num+1):
i, = f1.split("#")
f1.close()
except:
print("--------Loading-------")
time.sleep(1)
while True:
os.system("cls")
user_choice = ""
print("Main Menu" + "\n" + "---------")
print("1) Write")
print("2) Read")
print("3) Exit")
while user_choice not in ["1", "2", "3"]:
user_choice = input("Pick 1, 2 or 3 \n")
if user_choice == "1":
write()
elif user_choice == "2":
read()
else:
exit()
if __name__ == "__main__":
main()
It might be too complicated to understand in which case just ask me in comments- otherwise general tips would be nice aswell.
Thanks in advance
A quick point of correction:
global is only required if you're defining a global variable inside a non-global context. In other words, anything defined at the default indentation level, will be accessible by everything else defined below it. For example:
def set_global():
x = 1
def use_global():
x += 1
set_global()
try:
use_global()
except Exception as e:
# `use_global` doesn't know
# about what `set_global` did
print("ERROR: " + str(e))
# to resolve this we can set `x` to a
# default value in a global context:
x = 1
# or, if it were required, we
# could create a global variable
def make_global():
global x
make_global()
# either will work fine
set_global()
use_global()
print(x) # prints 2
Now to the actual question:
I haven't read through the block of code you wrote (probably best to trim it down to just the relevant bits in the future), but this should solve the problem as I understand it, and you described it.
import os
import sys
user_text = []
# login the user somehow
user_file = 'saves.txt'
def writelines(f, lines):
"""Write lines to file with new line characters"""
f.writelines('\n'.join(lines))
def readlines(f):
"""Get lines from file split on new line characters"""
text = f.read()
return text.split('\n') if text else []
class _Choice(object):
"""Class that is equivalent to a set of choices
Example:
>>> class YesObj(Choice):
>>> options = ('y', 'yes')
>>> Yes = YesObj()
>>> assert Yes == 'yes'
>>> assert Yes == 'y'
>>> # assertions evaluate to True
Override the `options` attribute to make use
"""
allowed = ()
def __eq__(self, other):
try:
s = str(other)
except:
raise TypeError("Cannot compare with non-string")
else:
return s.lower() in self.allowed
def _choice_repr(choices):
allowed = []
for c in choices:
if isinstance(c, _Choice):
allowed.extend(c.allowed)
else:
allowed.append(c)
if len(allowed) > 2:
s = ', '.join([repr(c) for c in allowed[:-1]])
s += ', or %s' % repr(allowed[-1])
elif len(allowed) == 1:
s = '%s or %s' % allowed
else:
s = '%s' % allowed[0]
return s
def _choice_sentinel(name, allowed):
"""Creates a sentinel for comparing options"""
return type(name, (_Choice,), {'allowed': list(allowed)})()
Quit = _choice_sentinel('Quit', ('q', 'quit'))
Yes = _choice_sentinel('Yes', ('y', 'yes'))
No = _choice_sentinel('No', ('n', 'no'))
def readline_generator(f):
"""Generate a file's lines one at a time"""
t = f.readline()
# while the line isn't empty
while bool(t):
yield t
t = f.readline()
def read_from_cache():
"""Overwrite `user_text` with file content"""
if not os.path.isfile(user_file):
open(user_file, 'w').close()
globals()['user_text'] = []
else:
with open(user_file, 'r') as f:
lines = readlines(f)
# replace vs extend user text
for i, t in enumerate(lines):
if i == len(user_text):
user_text.extend(lines[i:])
else:
user_text[i] = t
def write_to_cache():
"""Overwrite cache after the first line disagrees with current text
If modifications have been made near the end of the file, this will
be more efficient than a blindly overwriting the cache."""
with open(user_file, 'r+') as f:
i = -1
last_pos = f.tell()
# enumerate is a generator, not complete list
for i, t in enumerate(readline_generator(f)):
if user_text[i] != t:
# rewind to the line before
# this diff was encountered
f.seek(last_pos)
# set the index back one in
# order to catch the change
i -= 1
break
last_pos = f.tell()
# then cut off remainder of file
f.truncate()
# recall that i is the index of the diff
# replace the rest of it with new
# (and potentially old) content
writelines(f, user_text[i+1:])
def blind_write_to_cache():
"""Blindly overwrite the cache with current text"""
with open(user_file, 'w') as f:
writelines(f, user_text)
def overwrite_user_text(i, text, save=False):
"""Overwrite a line of text
If `save` is True, then these changes are cached
"""
try:
user_text[i] = text
except IndexError:
raise IndexError("No text exists on line %r" % (i+1))
if save:
write_to_cache()
def user_input():
"""Get a new line from the user"""
return raw_input("input text: ")
def user_choice(msg, choices):
if len(choices) == 0:
raise ValueError("No choices were given")
ans = raw_input(msg)
if ans not in choices:
print("Invalid Response: '%s'" % ans)
m = "Respond with %s: " % _choice_repr(choices)
return user_choice(m, choices)
else:
return ans
def user_appends():
"""User adds a new line"""
user_text.append(user_input())
def user_reads(*args, **kwargs):
"""Print a set of lines for the user
Selects text via `user_text[slice(*args)]`
Use 'print_init' in kwargs to choose how
many lines are printed out before user must
scroll by pressing enter, or quit with 'q'."""
print_init = kwargs.get('print_init', 4)
sliced = user_text[slice(*args)]
if not isinstance(sliced, list):
sliced = [sliced]
for i, l in enumerate(sliced):
if i < print_init:
print(l)
sys.stdout.flush()
elif user_choice(l, ['', Quit]) == Quit:
break
def user_changes(i=None, save=False):
"""User changes a preexisting line"""
attempt = True
while i is None and attempt:
# get the line the user wants to change
i_text = raw_input("Line to be changed: ")
try:
# make user input an index
i = int(i_text)
except:
# check if they want to try again
c = user_choice("Bad input - '%s' is not an "
"integer. Try again? " % i_text, (Yes, No))
attempt = (c == Yes)
if attempt:
# user gave a valid integer for indexing
try:
user_reads(i-1)
overwrite_user_text(i-1, user_input(), save)
except Exception as e:
print("ERROR: %s" % e)
if user_choice("Try again? ", (Yes, No)):
user_changes(i, save)
# stores whatever text is already on
# file to `user_text` before use
read_from_cache()

Categories