0

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

P.Gracia
  • 214
  • 1
  • 2
  • 9
  • You are creating a **shallow** copy of lists, the nested lists are not copied and so are shared between outer lists. – Martijn Pieters Mar 17 '19 at 11:34
  • `sorted_list = [sub[:] for sub in mysorted_list]` would create copies of the next level of lists, putting them in a new list with a list comprehension. – Martijn Pieters Mar 17 '19 at 11:35

0 Answers0