0
\$\begingroup\$

I am working on a problem that simply calculates and prints the credit card balance. I hope this code can be improved/optimized a lot.

I am from a Java background. If it was Java code, I would have pulled all the code lines inside the for loop and place it at the beginning of the method and inside for loop I would call the method recursively. I tried to do the same in python but it hit me with some error:

RuntimeError: maximum recursion depth exceeded while calling a Python object

From a quick internet search I found that it is better to go for iteration instead of recursion:

'''
Program to calculate credit card balance after 1 year
'''
def calculateCreditCardBalance(balance, annualInterestRate, monthlyPaymentRate):

    totalPaid = 0

    for thisMonth in range(1,13):
        monthlyInterestRate = annualInterestRate / 12.0
        minimumMonthlyPayment = round(monthlyPaymentRate * balance, 2)
        monthlyUnpaidBalance = balance - minimumMonthlyPayment
        updatedBalanceEachMonth = round(monthlyUnpaidBalance + (monthlyInterestRate * monthlyUnpaidBalance) , 2) 

        print 'Month: %d' %(thisMonth)
        print 'Minimum monthly payment: ' + str(minimumMonthlyPayment)
        print 'Remaining balance: ' + str(updatedBalanceEachMonth)

        balance = updatedBalanceEachMonth
        totalPaid += minimumMonthlyPayment

    print 'Total paid: ' + str(totalPaid)
    print 'Remaining balance: '+ str(balance)


calculateCreditCardBalance(4842, 0.2, 0.04)
\$\endgroup\$
1
  • \$\begingroup\$ Please read How to Ask, and edit your question and its title to describe what this code does. Also, if it is an online programming-challenge, add that tag and cite the challenge. \$\endgroup\$ – 200_success Jun 19 '16 at 16:40
1
\$\begingroup\$

A good thing you can do is to create more functions, which return values in the dictionary form.

Pseudo-code:

def calculateCreditCardBalance(balance, annual_interest_rate, monthly_payment_rate):
    total_paid = 0
    curr_values = ["balance" => balance, "annual_interest_rate" => annual_interest_rate, "monthly_payment_rate" => monthly_payment_rate]
    foreach this_month in rage(1,13):
        month_values = calculate_month_values(curr_values)
        print_values(month_values)
        update_month_values(month_values,curr_values)
    print 'Total paid: ' + str(curr_values["total_paid"])
    print 'Remaining balance: '+ str(curr_values["total_paid"])
\$\endgroup\$
1
\$\begingroup\$

There exists a style guide for python, PEP8, which programmers are encouraged to follow. It recommends lower_case variable and function names. While doing that I would choose slightly shorter (but still descriptive) names to improve readability.


If you move your string describing the function into the function definition itself, it becomes a docstring. This allows you e.g to call help(function_name) to get a description of the function.


Try to avoid using string addition because it creates an additional temporary variable for each addition. Rather use format (with its syntax described here). You can also use multi-line strings here.


I would move the calculation of the monthly interest rate out of the loop, it does not change.


Avoid unused temporary variables. The variable updatedBalanceEachMonth is only used for printing and assigning balance to its value at the end. Rather update balance right away.


The calculation of the new balance can also be simplified somewhat from:

monthlyUnpaidBalance = balance - minimumMonthlyPayment
        updatedBalanceEachMonth = round(monthlyUnpaidBalance + (monthlyInterestRate * monthlyUnpaidBalance) , 2) 

to:

    updatedBalanceEachMonth = round((1 + monthlyInterestRate) * (balance - minimumMonthlyPayment), 2) 

This way, the intermediate variable is no longer needed.

If you did not want to print the minimum payment, it could even be simplified further:

balance = round((1 + interest_rate) * (1 - payment_rate) * balance, 2)

I would make the function return either the new balance or the total amount paid. This way it could be used elsewhere. For this to be practical, you should maybe make the printing optional and guard the test call guarded by a if __name__ == "__main__": clause.

Final code:

def credit_card_balance(balance, interest_rate, payment_rate, verbose=False):
    '''Return credit card balance after 1 year'''
    total = 0
    interest_rate /= 12.0
    for month in range(12):
        minimum_payment = round(payment_rate * balance, 2)
        total += minimum_payment
        balance = round((1 + interest_rate) * (balance - minimum_payment), 2)
        if verbose:
            print """Month: {}
Minimum monthly payment: {}
Remaining balance: {}
""".format(month+1, minimum_payment, balance)

    if verbose:
        print 'Total paid: {}'.format(total)
    return balance

if __name__ == "__main__":
    balance = 4842
    balance = credit_card_balance(balance, 0.2, 0.04, True)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.