Just for the sake of checking my knowledge with Python I decided to give it a try to some coding challenges I've seen online. One of these challenges is about creating an expense divider, that I have tried to do by using OOP (I'm kind of new in coding and self-taught, so I ask for apologies if I'm using some term wrong). My issue comes when I see that one of the variables (a list) that I expected not to change, is changing every time I execute a method of a class. I will expose my whole code, and after it point at the lines that I think I am having problems with:
import numpy as np
import time
class User():
'''
This class represents the different users that are going to be participating in the payment
It basically uses a name of the user, an identifier to locate him, and the money that has invested
There are two methods, one setter and one getter:time
The setter augments the capital of the user by inputting money
The getter gets the aumount of money invested by that user
´'''
def __init__(self,name, ID):
self.name = name
self.id = ID
self.capital = 0
def set_payment(self, amount):
self.capital += amount
def get_capital(self):
return self.capital
def get_ID(self):
return self.id
class GroupOfUsers():
'''
This class represents a group of users that are going to participate in the group payments
'''
def __init__(self, *all_user):
aux_dictionary = {}
for user in all_user:
aux_dictionary[user.name] = {'ID': user.get_ID(), 'capital':user.get_capital()}
self.identifier = aux_dictionary
def set_payment(self,amount,payer,*payee):
global my_Debt
number_payees = 0
for person in payee:
number_payees += 1
divided_amount = amount/number_payees
self.identifier[payer]['capital'] += amount
for person in payee:
self.identifier[person]['capital'] -= divided_amount
my_Debt = Debt(my_Group.identifier)
class Debt():
def __init__(self,users):
users_capital = []
for key, value in users.items():
users_capital.append([key, value['capital']])
self.users_capital = users_capital
def get_capital(self):
return self.users_capital
def get_debtors(self):
mysorted_list = sorted(self.get_capital(), key=lambda item: item[1])
sorted_list = mysorted_list[:]
print(sorted_list)
while len(sorted_list) > 1:
payer = sorted_list[0]
payee = sorted_list[-1]
if abs(payer[1]) <= abs(payee[1]):
print('{} has to pay {} the amount of {}.'.format(payer[0],payee[0],abs(payer[1])))
sorted_list[-1][1] -= abs(payer[1])
sorted_list.pop(0)
elif abs(payer[1]) > abs(payee[1]):
print('{} has to pay {} the amount of {}.'.format(payer[0],payee[0],abs(payee[1])))
sorted_list[0][1] += abs(payee[1])
sorted_list.pop(-1)
while True:
num_users = input('How many users are participating?')
try:
int_num_users = int(num_users)
if int_num_users < 1:
print ('Introduce a positive number of people')
time.sleep(0.5)
continue
else:
float_num_users = float(num_users)
if np.sqrt((int_num_users - float_num_users)**2)/float_num_users <= 0.0001:
print('Debugging... Everything seems fine')
break
else:
print('Introduce a natural number (no decimal and positive number)')
time.sleep(0.5)
continue
except:
print('Introduce a natural number')
print('Debugging... The number of users is {}.'.format(num_users))
num_users = int(num_users)
user_list = []
for numb in range(1,num_users+1):
user_name = input('What is the name of the user number {}? '.format(numb))
myUser = User(name = user_name, ID = numb-1)
user_list.append(myUser)
my_Group = GroupOfUsers(*user_list)
my_Debt = Debt(my_Group.identifier)
That above is basically my code. One can enter names as an input (John, Peter, Maria, Ana in my case) and those will be created as the users of the expenditure, and some group and debt class will automatically be created.
Some new expenses can be introduced by:
my_Group.set_payment(50,'Maria','Peter')
my_Group.set_payment(100,'Ana','John','Peter')
my_Group.set_payment(200,'John','Ana')
And when checking if it has been defined properly:
my_Group.identifier
This is the outcome (as expected):
{'John': {'ID': 0, 'capital': 150.0},
'Peter': {'ID': 1, 'capital': -100.0},
'Maria': {'ID': 2, 'capital': 50},
'Ana': {'ID': 3, 'capital': -100.0}}
My issue comes with the 'get_debtors' method in the Debt class. As it is defined, it creates an auxiliary list ('sorted_list') and operates with it, providing some outputs of who owes whom money. If I use this mehtod to check on the results:
my_Debt.get_debtors()
The outcome is:
[['Peter', -100.0], ['Ana', -100.0], ['Maria', 50], ['John', 150.0]]
Peter has to pay John the amount of 100.0.
Ana has to pay John the amount of 50.0.
Ana has to pay Maria the amount of 50.0.
Yet, (and this is my problem, sorry for the long intro), if I run this line again (I am using Jupyter Notebook) without changing anything in the program, I get this output:
[['Peter', -100.0], ['Ana', -50.0], ['Maria', 0.0], ['John', 50.0]]
Peter has to pay John the amount of 50.0.
Peter has to pay Maria the amount of 0.0.
Peter has to pay Ana the amount of 50.0.
Which means that my method has changed some variable and it is reading a modified data. I have tried to find out where my problem is, but I am clueless, and I can't see the root of it. In my limited experience I thought that by using a = b[:] being 'a' and 'b' lists, 'a' would be a copy of 'b', but it wouldn't modify 'b' at all if I change somehting in 'a', and that's why I tried with that 'sorted_list = mysorted_list[:]', and yet it seems to be changing, so there is something else I am not seeing. Does anybody have any idea why is this happening, and how I can address it so I don't get stuck again in the future with this?
Thank you very much