How to solve Unbound Local error in my conditional block - python-3.x

I need to find the discount of an order
I used the following code to make the string input of order numbers to a dictionary and thenused sum() to find the total of the order
However I wish to have an offer discount if there is one-1 and one-3 and (one-4 or one-5 or one-6)
But then after the conditional block when I want to multiply it I receive an Unbound Error
def compute_cost(order):
"""
Function 2: compute_cost(order)
Parameters: order (String)
Return: Final cost of order
"""
numcount = {}
orderlist = map(int, order)
for i in orderlist:
if numcount.get(i):
numcount[i] += 1
else:
numcount[i] = 1
for i in numcount:
if i == 1:
numcount[i] = numcount[i]*4.25
elif i == 2:
numcount[i] = numcount[i]*2.50
elif i == 3:
numcount[i] = numcount[i]*2.00
elif i == 4:
numcount[i] = numcount[i]*1.25
elif i == 5:
numcount[i] = numcount[i]*1.50
elif i == 6:
numcount[i] = numcount[i]*1.75
elif i == 7:
numcount[i] = numcount[i]*3.75
else:
return print("Your order has a number outside of the range (1:7)")
order_total = sum(numcount.values())
if(numcount[1] == 1 and
numcount[3] == 1 and
(numcount[4] == 1 or
numcount[5] == 1 or
numcount[6] == 1)):
discount1 = 0.20
order_total1 = order_total*discount1
return order_total1
Please help me
Thank you for your time and effort
EDIT
If you have a better way for me to find the values and save them in a dictionary I am open to constructive criticism too

Depending on the input, the numcount-dict may or may not have all the keys.
Case 1: UnboundLocalError
When calling the function with compute_cost('12323123'), the numcount-dict becomes:
{1: 2, 2: 3, 3: 3}
The if-statement first checks if numcount[1] == 1, which evaluates to False. Therefore the whole expression is False and Python doesn't even (need to) check the rest. (This is called short-circuit evaluation.)
Because the if-statement evaluates to False, discount1 is not set at all, so you get UnboundLocalError: local variable 'discount1' referenced before assignment.
Solution:
Add an else-clause that sets discount1 to 1 (= no discount) when the condition is False:
if (numcount[1] == 1 and numcount[3] == 1 and (numcount[4] == 1 or
numcount[5] == 1 or numcount[6] == 1)):
discount1 = 0.20
else:
discount1 = 1
Case 2: KeyError
Now, when calling the function with compute_cost('32235664'), the numcount-dict becomes:
{3: 2, 2: 2, 5: 1, 6: 2, 4: 1}
The if-statement first checks if numcount[1] == 1, but that key does not exist so Python raises a KeyError. Depending on your input and how far Python needs to evaluate the if-statement, you may get that KeyError or not.
Solution:
Make sure that the numcount-dict contains all keys from the beginning. You already know how many items the dict must have because you limit your input to the range (1:7). Therefore you can initalize the dict as:
numcount = {1:0, 2:0, 3:0, 4:0, 5:0, 6:0, 7:0}
EDIT: is there a better way?
Sure there is, there almost always is a better way:
prices = {'1':4.25, '2':2.50, '3':2.00, '4':1.25, '5':1.50, '6':1.75, '7':3.75}
orders = '12323456'
total = sum(prices[order] for order in orders)
if (all(orders.count(type) >= 1 for type in '13') and # multiple ANDs with 'all'
any(True for type in '456' if orders.count(type) >=1)): # multiple ORs with 'any'
discount = 0.2
else:
discount = 1
print('Order value: {}\nDiscount: {}\nOffer: {:.2f}'.format(total, discount, discount * total))
You now can easily extend your prices dictionary or the conditions for a discount. I assumed that the condition for a discount is that at least one item was ordered, not exactly one. Therefore I used >=1 which you can change into ==1 if it needs to be exactly one.

Related

Trying to tie the selection input so that I can press 1,2 or 3 and get my printed text options

def cSelection():
Selection = input()
return Selection
if Selection == 1 :
print('Oxygen levels are normal')
elif Selection == 2:
print('Fuel levels are at medium capacity')
elif Selection == 3:
print('Food is running low, request for new shipment')
The return statement is not in right place. Your if/elif conditions don't running due to the return returns from the function before them. The return should be after the logic (if/elif).
Furthermore the input() function returns a string type but your if/elif contidions wait an integer. It means your should cast the output of input() to integer with int().
I recommend to define an else branch if the input is not 1-3. Like in my below example.
Correct code:
def cSelection():
Selection = int(input("Write a number (1-3): ")) # Input cast to integer.
if Selection == 1 :
print('Oxygen levels are normal')
elif Selection == 2:
print('Fuel levels are at medium capacity')
elif Selection == 3:
print('Food is running low, request for new shipment')
else:
print("Wrong option")
return Selection
return_value = cSelection()
print("Return value of function: {}".format(return_value))
Output:
>>> python3 test.py
Write a number (1-3): 1
Oxygen levels are normal
Return value of function: 1
>>> python3 test.py
Write a number (1-3): 3
Food is running low, request for new shipment
Return value of function: 3
>>> python3 test.py
Write a number (1-3): 5
Wrong option
Return value of function: 5

How to fix this loop for special cases in Python

So I got assigned a homework where I have to do the convexHull problem by brute force (this is to then later compare the complexity vs the normal complexHull), and I have this code:
def determineSideLocation(A,B,P):
#Takes point A and B and creates a vector (A ---> B), direction sensitive
d = ((P[0] - A[0])*(B[1] - A[1])) - ((P[1] - A[1])*(B[0] - A[0]))
if d < 0:
#To the left of AB
return -1
elif d > 0:
#To the right of AB
return 1
elif d == 0:
#On AB
return 0
And now I determine if all the points I want to compare are on one side or not:
def isAllPointsOnOneSide(vector, pointList):
pointSideList = []
print("vector: " + str(vector))
print("pointList: " + str(pointList))
for i in pointList:
pointSideList.append(determineSideLocation(vector[0], vector[1], i))
print("pointSideList: " + str(pointSideList))
for j in range(0, len(pointSideList) - 1):
if pointSideList[j] == 0:
continue
elif pointSideList[j+1] == 0:
#2 options:
#1. pointSideList[j+1] is at the end of the list (j+1 = len(pointSideList) - 1)
#2. pointSideList[j+1] is not at the end of the list
if j+1 == (len(pointSideList) - 1):
continue
else:
if pointSideList[j+2] == 0:
if j+2 == (len(pointSideList) - 1):
continue
else:
if pointSideList[j] != pointSideList[j+3]:
return False
else:
continue
elif pointSideList[j] != pointSideList[j+2]:
return False
else:
continue
elif pointSideList[j] != pointSideList[j+1]:
return False
else:
continue
return True
There is the convexHull function but its not where the problem lies. The thing is: in the isAllPointsOnOneSide function, it takes the list of points and makes a list of values relative to the vector, which can only be 1 (to the right), -1 (to the left) and 0 (in the vector).
I got an ankward case where the relative values were this: [-1,0,0,0,0] and the function breaks itself and gives a False value despite being True (all points are on the vector or on one side). I know its in the exception part, where it tries to skip comparing the current value to a 0, but I know its not ideal to keep writing more exceptions to that part.
How can I fix it to avoid the cases where the function breaks? Where it has to compare with 0 at the end of the list
#Heike gave me the answer, and it was rather simple :P
def isAllPointsOnOneSide(vector, pointList):
pointSideList = []
for i in pointList:
pointSideList.append(determineSideLocation(vector[0], vector[1], i))
minValue = min(pointSideList)
maxValue = max(pointSideList)
#If max - min is higher than 1, its a False result.
if maxValue - minValue > 1:
return False
else:
return True
I'm sorry, and after all it was a rather simple problem, but I have been on this for several hours now (brute forcing the convexhull) and it frustated me.
Thanks to Heike again

Strange behaviour when adding values to elements of a python list

I'm trying to create a game which randomly chooses a "room" layout from a set and then puts it in 1 of 4 positions on the screen. My code to achieve this is:
room_spawns = [[randint(0, 5)] for a in range(4)]
for i in range(0, 4):
these_segments = []
these_shapes = []
if i == 1:
modifier = [800, 0]
elif i == 2:
modifier = [0, 448]
elif i == 3:
modifier = [800, 448]
else:
modifier = [0, 0]
if room_spawns[i] == 0:
these_segments = room_1_segments
these_shapes = room_1_shapes
elif room_spawns[i] == 1:
these_segments = room_2_segments
these_shapes = room_2_shapes
elif room_spawns[i] == 2:
these_segments = room_3_segments
these_shapes = room_3_shapes
elif room_spawns[i] == 3:
these_segments = room_4_segments
these_shapes = room_4_shapes
elif room_spawns[i] == 4:
these_segments = room_5_segments
these_shapes = room_5_shapes
elif room_spawns[i] == 5:
these_segments = room_6_segments
these_shapes = room_6_shapes
for z in range(0, len(these_segments)):
these_segments[z][0] += modifier[0]
these_segments[z][1] += modifier[1]
for x in range(0, len(these_shapes)):
these_shapes[x][0][0] += modifier[0]
these_shapes[x][0][1] += modifier[1]
these_shapes[x][1][0] += modifier[0]
these_shapes[x][1][1] += modifier[1]
segments.append(these_segments)
shapes.extend(these_shapes)
However, whenever a room which is the same as one chosen previously is chosen, its value is increased by the modifier value more than once and as such doesn't appear on screen. Can anybody please explain why this happens, and how it could be fixed.
Edit: After further research and debugging, for some reason, when adding the modifier value to these_segments and these_shapes, that same value is also added to the identical values already in segments and shapes, as seen here. It appears as though the values of room_2_segments themselves have been modified. Does anybody know why this is and how it could be prevented?
Notice that room_spawns is a list with length of one (it contains one boolean). Thus, room_spawns[0] will return that boolean, but for i>0 you get an IndexError.
Also, note that [randint(0, 5)] in range(4) will always return False because [randint(0,5)] is not an integer, it's a list with only one element. And since room_spawns is a list with this boolean, it will always be equal to [False].
Maybe you want room_spawns to be a list with more than one element?

Update list in for loop using list.append

The variable mult is not updated as the program runs. What is the issue with this code? The run results show me that the loop is actually working as I wanted but for the list update and final print
number = 18
for i in range(int(number/2)):
i += 1
mults = []
if number % i == 0:
mults = mults.append(i)
print(i)
elif number % i != 0:
pass
elif i == int(number/2):
print(mults)
with this other code I get the error: AttributeError: 'NoneType' object has no attribute 'append'
number = 18
mults = []
for i in range(int(number/2)):
i += 1
if number % i == 0:
mults = mults.append(i)
print(i)
elif number % i != 0:
pass
print(mults)
number = 18
mults = []
for i in range(int(number/2)):
i += 1
if number % i == 0:
mults.append(i)
print(i)
elif number % i != 0:
pass
print(mults)
Few notes, move mults outside of the for loop so you aren't over writing it every time the loop runs.
You don't need that last elif statement, just print(mults) when the for loop is done, is basically the last elif statement.
mults.append(i) is in line meaning it changes the list mults automatically and you don't need to reassign it.

Comparing file lines to strings in python, and inconsistencies

Important things this code is supposed to in order of execution:
1.Open and read the file "Goods"
2.Assign a random line from file "Goods" to the dictionary "goods"
3.Go through an if block that will assign a random value to the dictionary "cost" if goods[x] equals the string it's being compared to.
4.Print "goods", and "cost"
5.Repeat steps 2-4, 2 more times.
from random import randint
print("You search for things to buy in the market, and find:")
f = open('Goods', 'r') #Opens file "Goods"
lines = f.readlines() #Loads all lines from "Goods"
goods = {1:"", 2:"", 3:""}
cost = {1:"", 2:"", 3:""}
x = 0
while x < 3:
x += 1
goods[x] = lines[randint(0, 41)].strip()
#Checks to see if goods[x] is equal to the string on the right, if it is, it assigns cost[x] to a random integer
if goods[x] == "Lumber":
cost[x] = randint(2, 3)
elif goods[x] == "Rum":
cost[x] == randint(3, 4)
elif goods[x] == "Spices":
cost[x] = randint(4, 5)
elif goods[x] == "Fruit":
cost[x] == randint(2, 4)
elif goods[x] == "Opium":
cost[x] == randint(1, 5)
findings = '%s for %s gold.' %(goods[x], cost[x])
print(findings)
The problem with this code is that the dictionary:"cost" does not get a value assigned from the if block when goods[x] equals: Rum, Fruit, or Opium.
Could someone please tell me what's going on here?
The file "Goods"
Your problem is that you are using two equal signs. cost[x] == randint(3, 4) you need to use just one. Hope this helps!

Resources