Change-due functionImplementation of the change making algorithmCalculating change in AdaConverting money into changeCoin change algorithmChange-Making Problem SolutionCoin change with Dynamic ProgrammingCoin Change Problem with MemoizationVending machine change functionChange return programCalculate change denominations 3

Twelve Labours #08 - Dark Horse Bookmakers

Plot Dini's surface

Conservation of momentum in photon-atom collision

What is :>filename.txt Doing?

Was it possible for a message from Paris to reach London within 48 hours in 1782?

Python's .split() implemented in C

Automatically verify passwords for classroom exercise?

Are there any dishes that can only be cooked with a microwave?

N-Dimensional Cartesian Product

Which of these will work? HDMI to VGA or HDMI to USB?

How to delete a game file?

Why are rain clouds darker?

UK visitors visa needed fast for badly injured family member

Days in indexed month

SSH host identification changes on one wireless network

Nested lightning:button onClick Not Firing

What does this docker log entry mean?

What does Yoda's species eat?

What are the minimum element requirements for a star?

Is "montäglich" commonly used?

Is it possible to change paper title after send back from reviewers for revising?

doubt between Past Continuous and Past Simple

How to get rid of vertical white lines in a table?

Why do some DSLRs have ISO less than 100?



Change-due function


Implementation of the change making algorithmCalculating change in AdaConverting money into changeCoin change algorithmChange-Making Problem SolutionCoin change with Dynamic ProgrammingCoin Change Problem with MemoizationVending machine change functionChange return programCalculate change denominations 3






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;









9















$begingroup$


I wrote a function to calculate the change due from a transaction. It also prints the number of twenties, tens, fives, ones, and coins required to meet the amount. I am looking for suggestions for a more simplified and pythonic approach.



def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =

if change > 0:

twenties = change // 20.00
change_dict['Twenties'] = twenties
change -= 20.00 * twenties

tens = change // 10.00
change_dict['Tens'] = tens
change -= 10.00 * tens

fives = change // 5.00
change_dict['Fives'] = fives
change -= 5.00 * fives

ones = change // 1.00
change_dict['Ones'] = ones
change -= 1.00 * ones

quarters = change // 0.25
change_dict['Quarters'] = quarters
change -= 0.25 * quarters

dimes = change // 0.10
change_dict['Dimes'] = dimes
change -= 0.10 * dimes

nickels = change // 0.05
change_dict['Nickels'] = nickels
change -= 0.05 * nickels

pennies = change // 0.01
change_dict['Pennies'] = pennies
change -= 0.01 * pennies

else:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))









share|improve this question











$endgroup$










  • 3




    $begingroup$
    I don't have time for a full review, but wanted to mention that the general change making problem (use the smallest number of coins of provided denominations to make some sum) isn't correctly solved by always using the largest available coin. It works for standard US coins, but consider the case if some strange currency had 20, 9 and 1 and you want 37. Four 9s and a 1 is better than a 20, a 9 and eight 1s.
    $endgroup$
    – Josiah
    Sep 8 at 6:50










  • $begingroup$
    I don't know whether you care about hypothetical other currency or are just thinking about dollars at the moment.
    $endgroup$
    – Josiah
    Sep 8 at 6:51

















9















$begingroup$


I wrote a function to calculate the change due from a transaction. It also prints the number of twenties, tens, fives, ones, and coins required to meet the amount. I am looking for suggestions for a more simplified and pythonic approach.



def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =

if change > 0:

twenties = change // 20.00
change_dict['Twenties'] = twenties
change -= 20.00 * twenties

tens = change // 10.00
change_dict['Tens'] = tens
change -= 10.00 * tens

fives = change // 5.00
change_dict['Fives'] = fives
change -= 5.00 * fives

ones = change // 1.00
change_dict['Ones'] = ones
change -= 1.00 * ones

quarters = change // 0.25
change_dict['Quarters'] = quarters
change -= 0.25 * quarters

dimes = change // 0.10
change_dict['Dimes'] = dimes
change -= 0.10 * dimes

nickels = change // 0.05
change_dict['Nickels'] = nickels
change -= 0.05 * nickels

pennies = change // 0.01
change_dict['Pennies'] = pennies
change -= 0.01 * pennies

else:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))









share|improve this question











$endgroup$










  • 3




    $begingroup$
    I don't have time for a full review, but wanted to mention that the general change making problem (use the smallest number of coins of provided denominations to make some sum) isn't correctly solved by always using the largest available coin. It works for standard US coins, but consider the case if some strange currency had 20, 9 and 1 and you want 37. Four 9s and a 1 is better than a 20, a 9 and eight 1s.
    $endgroup$
    – Josiah
    Sep 8 at 6:50










  • $begingroup$
    I don't know whether you care about hypothetical other currency or are just thinking about dollars at the moment.
    $endgroup$
    – Josiah
    Sep 8 at 6:51













9













9









9





$begingroup$


I wrote a function to calculate the change due from a transaction. It also prints the number of twenties, tens, fives, ones, and coins required to meet the amount. I am looking for suggestions for a more simplified and pythonic approach.



def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =

if change > 0:

twenties = change // 20.00
change_dict['Twenties'] = twenties
change -= 20.00 * twenties

tens = change // 10.00
change_dict['Tens'] = tens
change -= 10.00 * tens

fives = change // 5.00
change_dict['Fives'] = fives
change -= 5.00 * fives

ones = change // 1.00
change_dict['Ones'] = ones
change -= 1.00 * ones

quarters = change // 0.25
change_dict['Quarters'] = quarters
change -= 0.25 * quarters

dimes = change // 0.10
change_dict['Dimes'] = dimes
change -= 0.10 * dimes

nickels = change // 0.05
change_dict['Nickels'] = nickels
change -= 0.05 * nickels

pennies = change // 0.01
change_dict['Pennies'] = pennies
change -= 0.01 * pennies

else:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))









share|improve this question











$endgroup$




I wrote a function to calculate the change due from a transaction. It also prints the number of twenties, tens, fives, ones, and coins required to meet the amount. I am looking for suggestions for a more simplified and pythonic approach.



def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =

if change > 0:

twenties = change // 20.00
change_dict['Twenties'] = twenties
change -= 20.00 * twenties

tens = change // 10.00
change_dict['Tens'] = tens
change -= 10.00 * tens

fives = change // 5.00
change_dict['Fives'] = fives
change -= 5.00 * fives

ones = change // 1.00
change_dict['Ones'] = ones
change -= 1.00 * ones

quarters = change // 0.25
change_dict['Quarters'] = quarters
change -= 0.25 * quarters

dimes = change // 0.10
change_dict['Dimes'] = dimes
change -= 0.10 * dimes

nickels = change // 0.05
change_dict['Nickels'] = nickels
change -= 0.05 * nickels

pennies = change // 0.01
change_dict['Pennies'] = pennies
change -= 0.01 * pennies

else:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))






python change-making-problem






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 8 at 2:57









200_success

138k21 gold badges175 silver badges448 bronze badges




138k21 gold badges175 silver badges448 bronze badges










asked Sep 8 at 2:32









hseekhseek

1716 bronze badges




1716 bronze badges










  • 3




    $begingroup$
    I don't have time for a full review, but wanted to mention that the general change making problem (use the smallest number of coins of provided denominations to make some sum) isn't correctly solved by always using the largest available coin. It works for standard US coins, but consider the case if some strange currency had 20, 9 and 1 and you want 37. Four 9s and a 1 is better than a 20, a 9 and eight 1s.
    $endgroup$
    – Josiah
    Sep 8 at 6:50










  • $begingroup$
    I don't know whether you care about hypothetical other currency or are just thinking about dollars at the moment.
    $endgroup$
    – Josiah
    Sep 8 at 6:51












  • 3




    $begingroup$
    I don't have time for a full review, but wanted to mention that the general change making problem (use the smallest number of coins of provided denominations to make some sum) isn't correctly solved by always using the largest available coin. It works for standard US coins, but consider the case if some strange currency had 20, 9 and 1 and you want 37. Four 9s and a 1 is better than a 20, a 9 and eight 1s.
    $endgroup$
    – Josiah
    Sep 8 at 6:50










  • $begingroup$
    I don't know whether you care about hypothetical other currency or are just thinking about dollars at the moment.
    $endgroup$
    – Josiah
    Sep 8 at 6:51







3




3




$begingroup$
I don't have time for a full review, but wanted to mention that the general change making problem (use the smallest number of coins of provided denominations to make some sum) isn't correctly solved by always using the largest available coin. It works for standard US coins, but consider the case if some strange currency had 20, 9 and 1 and you want 37. Four 9s and a 1 is better than a 20, a 9 and eight 1s.
$endgroup$
– Josiah
Sep 8 at 6:50




$begingroup$
I don't have time for a full review, but wanted to mention that the general change making problem (use the smallest number of coins of provided denominations to make some sum) isn't correctly solved by always using the largest available coin. It works for standard US coins, but consider the case if some strange currency had 20, 9 and 1 and you want 37. Four 9s and a 1 is better than a 20, a 9 and eight 1s.
$endgroup$
– Josiah
Sep 8 at 6:50












$begingroup$
I don't know whether you care about hypothetical other currency or are just thinking about dollars at the moment.
$endgroup$
– Josiah
Sep 8 at 6:51




$begingroup$
I don't know whether you care about hypothetical other currency or are just thinking about dollars at the moment.
$endgroup$
– Josiah
Sep 8 at 6:51










4 Answers
4






active

oldest

votes


















10

















$begingroup$

Firstly, when working with monetary values, to avoid floating-point-number-related issues (you can try calling change_return(1, 1.15) in your solution to see it), it is more common to either (1) use cent as the unit and store all values as integers; or (2) use the Decimal class.



Secondly, the repetition in the code can be avoided. The currencies can be stored and iterated over one by one in a for loop. A simple approach to store all kinds of currencies is to use a list as others have shown. Another option is to use enumerations.



Thirdly, the logic could be simplified a bit by using the modulo operation change % denom to calculate change - change // denom * denom.



Following is refactored code:



from enum import Enum
from decimal import Decimal

class Currency(Enum):
TWENTY = "20.00", "Twenties"
TEN = "10.00", "Tens"
FIVE = "5.00", "Fives"
ONE = "1.00", "Ones"
# more ...
PENNY = "0.01", "Pennies"

def __init__(self, denomination, print_name):
self.denomination = Decimal(denomination)
self.print_name = print_name

def change_return(cost, paid):
cost = Decimal(cost)
paid = Decimal(paid)

change = paid - cost
change_dict = # Before Python 3.6 dictionaries do not preserve order so a sorting may be needed before printing

if change < 0:
return None # Better to raise an exception if performance is not a major concern

# Better to do rounding here rather than before calculating the change
# round() could also be used here, quantize() offers more rounding options, if needed
precision = Decimal("0.01")
change = change.quantize(precision)

# Note that the iteration order follows the declaration order in the Currency Enum,
# therefore currencies should be declared in descending order of their denominations
# to yield the desired outcome
for cur in Currency:
currency_cnt, change = divmod(change, cur.denomination) # divmod(a, b) returns a tuple (a // b, a % b)
if currency_cnt: # Same as currency_cnt != 0
change_dict[cur] = currency_cnt
return change_dict

if __name__ == "__main__":
changes = change_return("30", "86.13")
if changes is None:
print('Insufficient funds')
else:
for cur, count in changes.items():
print(f"cur.print_name:<9: count") # Format output using format strings





share|improve this answer












$endgroup$














  • $begingroup$
    Good call out on Decimal or using ints and nice use of Enum. You could use a dict but you would have to sorted() the iterator unless you want to rely on Py3.6+ having ordered dictionaries. You don't really need the else after a return. I would be tempted to raise ValueError('Insufficient Funds') vs return None.
    $endgroup$
    – AChampion
    Sep 9 at 0:34










  • $begingroup$
    @AChampion Thanks for the comment. A few points: 1. It does rely on ordered dictionaries. A comment is added. I considered to return a list of tuples but eventually kept the dictionary just in case a lookup is needed on that. 2. I agree with you in this case but be aware that sometimes an explicit else can improve readablity. 3. I was also tempted to raise an error but eventually hold it back because of the potential performance overhead of handling exceptions and not knowing the full requirement.
    $endgroup$
    – GZ0
    Sep 9 at 1:23










  • $begingroup$
    I'm loving the divmod use. Perhaps add a note about the fact that the iteration order of for cur in Currencyis dependent on the declaration order in the Currency Enum? This is a gotcha in progressing code development here.
    $endgroup$
    – Gloweye
    Sep 9 at 14:25










  • $begingroup$
    @JaccovanDorp Thanks. A comment about that is added.
    $endgroup$
    – GZ0
    Sep 9 at 14:42



















6

















$begingroup$

Style



I suggest you check PEP0008 https://www.python.org/dev/peps/pep-0008/ the official Python style guide to give you some insights on how to write a Pythonic style code.



def change_return(cost,paid):



  • Docstrings: Python Docstring is the documentation string which is string literal, and it occurs in the class, module, function or method definition, and it is written as a first statement. Docstrings are accessible from the doc attribute for any of the Python object and also with the built-in help() function can come in handy. You should include a docstrings to your functions explaining what they do and what they return:



    def calculate_total_change(cost, paid):
    """Calculate change and return total"""
    # code



  • f-Strings PEP 498 introduced a new string formatting mechanism known as Literal String Interpolation or more commonly as F-strings (because of the leading f character preceding the string literal). ... In Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces and this facilitates the insertion of variables in strings.



    print(key + ': ' + str(value))


    can be written:



    print(f'key: value')


  • Blank lines: Too much blank lines in your function:


PEP008: Surround top-level function and class definitions with two blank lines.
Method definitions inside a class are surrounded by a single blank line.
Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).
Use blank lines in functions, sparingly, to indicate logical sections.



Code




  • A function should return a function usually returns something instead of printing.



    else:
    print('Insufficient funds')

    for key,value in change_dict.items():
    if value > 0:
    print(key + ': ' + str(value))


You might do the following instead:



return change_dict


Then use if __name__ == '__main__': guard at the end of your script which allows other modules to import your module without running the whole script like this:



if __name__ == '__main__': 
change = change_return(15, 20)
if change:
for unit, value in change.items():
print(f'value: unit')
if not change:
print('Amount paid is exact.')


Here's a refactored version of your code:



from fractions import Fraction


def calculate_total_change(cost, paid):
"""
cost: a float/int representing the total cost.
paid: a float/int representing amount paid
return: Total change (dictionary).
"""
units = [('Twenties', 20), ('Tens', 10), ('Fives', 5), ('Ones', 1), ('Quarters', Fraction(1 / 4)),
('Dimes', Fraction(1 / 10)), ('Nickels', Fraction(5 / 100)), ('Pennies', Fraction(1 / 100))]
total_change = unit[0]: 0 for unit in units
if cost > paid:
raise ValueError(f'Insufficient amount paid for cost cost.')
if cost == paid:
print('No change.')
return
if cost < paid:
change = paid - cost
if change:
for unit in units:
while change - unit[1] >= 0:
total_change[unit[0]] += 1
change -= unit[1]
return total_change


if __name__ == '__main__':
change = calculate_total_change(15, 22.5)
if change:
for unit, value in change.items():
print(f'unit: value')
else:
print('Amount exact')





share|improve this answer












$endgroup$










  • 1




    $begingroup$
    Is there a reason you use Fraction over Decimal? Also, your return value is inconsistent - if someone pays the exact amount you return an integer instead of a dict, which will break your loop in if __name__ == "__main__". I think it would be more consistent to return an empty dict there.
    $endgroup$
    – Gloweye
    Sep 10 at 6:48










  • $begingroup$
    thanks for pointing this out, I'll edit the code and there is no specific reason for choosing Fraction over Decimal, I guess both would do the same job, I'm more familiar with Fraction that's all and why returning a zero will break the loop?
    $endgroup$
    – user203258
    Sep 10 at 6:52











  • $begingroup$
    I think returning a zero would be inconsistent with what is indicated in the docstring however, it won't break the loop because both a zero and the empty dict have the same boolean value (False)
    $endgroup$
    – user203258
    Sep 10 at 7:00










  • $begingroup$
    Oh, right, didn't see that if change:. However, perhaps someone will use the function somewhere else - and having to check it would be a pain in that case.
    $endgroup$
    – Gloweye
    Sep 10 at 7:14


















5

















$begingroup$

I would clean it up by separating my declarations from my code. With the below code, it becomes immediately clear to the reader that you are working with the various denominations of US currency. I have to work to figure it out in your code. Also, this lends itself to using other currency. I could define a different denominations variable to work with different currencies without changing my code at all.



from collections import namedtuple

Denomination = namedtuple('Denomination', 'name value')
denominations = [
Denomination("Twenties", 20.00),
Denomination("Tens", 10.00),
Denomination("Fives", 5.00),
Denomination("Ones", 1.00),
Denomination("Quarters", 0.25),
Denomination("Dimes", 0.10),
Denomination("Nickles", 0.05),
Denomination("Pennies", 0.01)
]


def change_return(cost, paid):
change = round(paid-cost,2)
print(f'Change due: change')

if change > 0:
for d in denominations:
used = change // d.value
if used > 0:
print(d.name + ": " + str(d.value))
change -= d.value * used

else:
print('Insufficient funds')


if __name__ == '__main__':
change_return(30, 75.13)





share|improve this answer










$endgroup$










  • 5




    $begingroup$
    The poor person that gives exactly the right amount to the cashier/program. They are told that they have insufficient funds (it should probably be elif change < 0).
    $endgroup$
    – Graipher
    Sep 8 at 15:32


















5

















$begingroup$

You have a lot of repeating code (x = change // y).
To fix this, you can use the following function:




def get_denomination(change, denom):
num_of_denom = change // denom
return (num_of_denom, change - (num_of_denom * denom))
# New use-case
twenties, change = get_denomination(change, 20.00)



And, to avoid calculating the amount of every denomination in the case that change reaches zero before the end of your conditional block:




def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =
denominations = [20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01]
titles = ['Twenties', 'Tens', 'Fives', 'Ones', 'Quarters', 'Dimes',
'Nickels', 'Pennies']

for index, denomination in enumerate(denominations):
num_denom, change = get_denomination(change, denomination)
change_dict[titles[index]] = num_denom
if change == 0:
break

if change < 0:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))





share|improve this answer










$endgroup$














  • $begingroup$
    Why are you re-implementing the divmod() builtin? Also, it will be a bit better if you zip(titles, denominations) instead of enumerate + indexing.
    $endgroup$
    – Gloweye
    Sep 10 at 6:53












Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);














draft saved

draft discarded
















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f227646%2fchange-due-function%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown


























4 Answers
4






active

oldest

votes








4 Answers
4






active

oldest

votes









active

oldest

votes






active

oldest

votes









10

















$begingroup$

Firstly, when working with monetary values, to avoid floating-point-number-related issues (you can try calling change_return(1, 1.15) in your solution to see it), it is more common to either (1) use cent as the unit and store all values as integers; or (2) use the Decimal class.



Secondly, the repetition in the code can be avoided. The currencies can be stored and iterated over one by one in a for loop. A simple approach to store all kinds of currencies is to use a list as others have shown. Another option is to use enumerations.



Thirdly, the logic could be simplified a bit by using the modulo operation change % denom to calculate change - change // denom * denom.



Following is refactored code:



from enum import Enum
from decimal import Decimal

class Currency(Enum):
TWENTY = "20.00", "Twenties"
TEN = "10.00", "Tens"
FIVE = "5.00", "Fives"
ONE = "1.00", "Ones"
# more ...
PENNY = "0.01", "Pennies"

def __init__(self, denomination, print_name):
self.denomination = Decimal(denomination)
self.print_name = print_name

def change_return(cost, paid):
cost = Decimal(cost)
paid = Decimal(paid)

change = paid - cost
change_dict = # Before Python 3.6 dictionaries do not preserve order so a sorting may be needed before printing

if change < 0:
return None # Better to raise an exception if performance is not a major concern

# Better to do rounding here rather than before calculating the change
# round() could also be used here, quantize() offers more rounding options, if needed
precision = Decimal("0.01")
change = change.quantize(precision)

# Note that the iteration order follows the declaration order in the Currency Enum,
# therefore currencies should be declared in descending order of their denominations
# to yield the desired outcome
for cur in Currency:
currency_cnt, change = divmod(change, cur.denomination) # divmod(a, b) returns a tuple (a // b, a % b)
if currency_cnt: # Same as currency_cnt != 0
change_dict[cur] = currency_cnt
return change_dict

if __name__ == "__main__":
changes = change_return("30", "86.13")
if changes is None:
print('Insufficient funds')
else:
for cur, count in changes.items():
print(f"cur.print_name:<9: count") # Format output using format strings





share|improve this answer












$endgroup$














  • $begingroup$
    Good call out on Decimal or using ints and nice use of Enum. You could use a dict but you would have to sorted() the iterator unless you want to rely on Py3.6+ having ordered dictionaries. You don't really need the else after a return. I would be tempted to raise ValueError('Insufficient Funds') vs return None.
    $endgroup$
    – AChampion
    Sep 9 at 0:34










  • $begingroup$
    @AChampion Thanks for the comment. A few points: 1. It does rely on ordered dictionaries. A comment is added. I considered to return a list of tuples but eventually kept the dictionary just in case a lookup is needed on that. 2. I agree with you in this case but be aware that sometimes an explicit else can improve readablity. 3. I was also tempted to raise an error but eventually hold it back because of the potential performance overhead of handling exceptions and not knowing the full requirement.
    $endgroup$
    – GZ0
    Sep 9 at 1:23










  • $begingroup$
    I'm loving the divmod use. Perhaps add a note about the fact that the iteration order of for cur in Currencyis dependent on the declaration order in the Currency Enum? This is a gotcha in progressing code development here.
    $endgroup$
    – Gloweye
    Sep 9 at 14:25










  • $begingroup$
    @JaccovanDorp Thanks. A comment about that is added.
    $endgroup$
    – GZ0
    Sep 9 at 14:42
















10

















$begingroup$

Firstly, when working with monetary values, to avoid floating-point-number-related issues (you can try calling change_return(1, 1.15) in your solution to see it), it is more common to either (1) use cent as the unit and store all values as integers; or (2) use the Decimal class.



Secondly, the repetition in the code can be avoided. The currencies can be stored and iterated over one by one in a for loop. A simple approach to store all kinds of currencies is to use a list as others have shown. Another option is to use enumerations.



Thirdly, the logic could be simplified a bit by using the modulo operation change % denom to calculate change - change // denom * denom.



Following is refactored code:



from enum import Enum
from decimal import Decimal

class Currency(Enum):
TWENTY = "20.00", "Twenties"
TEN = "10.00", "Tens"
FIVE = "5.00", "Fives"
ONE = "1.00", "Ones"
# more ...
PENNY = "0.01", "Pennies"

def __init__(self, denomination, print_name):
self.denomination = Decimal(denomination)
self.print_name = print_name

def change_return(cost, paid):
cost = Decimal(cost)
paid = Decimal(paid)

change = paid - cost
change_dict = # Before Python 3.6 dictionaries do not preserve order so a sorting may be needed before printing

if change < 0:
return None # Better to raise an exception if performance is not a major concern

# Better to do rounding here rather than before calculating the change
# round() could also be used here, quantize() offers more rounding options, if needed
precision = Decimal("0.01")
change = change.quantize(precision)

# Note that the iteration order follows the declaration order in the Currency Enum,
# therefore currencies should be declared in descending order of their denominations
# to yield the desired outcome
for cur in Currency:
currency_cnt, change = divmod(change, cur.denomination) # divmod(a, b) returns a tuple (a // b, a % b)
if currency_cnt: # Same as currency_cnt != 0
change_dict[cur] = currency_cnt
return change_dict

if __name__ == "__main__":
changes = change_return("30", "86.13")
if changes is None:
print('Insufficient funds')
else:
for cur, count in changes.items():
print(f"cur.print_name:<9: count") # Format output using format strings





share|improve this answer












$endgroup$














  • $begingroup$
    Good call out on Decimal or using ints and nice use of Enum. You could use a dict but you would have to sorted() the iterator unless you want to rely on Py3.6+ having ordered dictionaries. You don't really need the else after a return. I would be tempted to raise ValueError('Insufficient Funds') vs return None.
    $endgroup$
    – AChampion
    Sep 9 at 0:34










  • $begingroup$
    @AChampion Thanks for the comment. A few points: 1. It does rely on ordered dictionaries. A comment is added. I considered to return a list of tuples but eventually kept the dictionary just in case a lookup is needed on that. 2. I agree with you in this case but be aware that sometimes an explicit else can improve readablity. 3. I was also tempted to raise an error but eventually hold it back because of the potential performance overhead of handling exceptions and not knowing the full requirement.
    $endgroup$
    – GZ0
    Sep 9 at 1:23










  • $begingroup$
    I'm loving the divmod use. Perhaps add a note about the fact that the iteration order of for cur in Currencyis dependent on the declaration order in the Currency Enum? This is a gotcha in progressing code development here.
    $endgroup$
    – Gloweye
    Sep 9 at 14:25










  • $begingroup$
    @JaccovanDorp Thanks. A comment about that is added.
    $endgroup$
    – GZ0
    Sep 9 at 14:42














10















10











10







$begingroup$

Firstly, when working with monetary values, to avoid floating-point-number-related issues (you can try calling change_return(1, 1.15) in your solution to see it), it is more common to either (1) use cent as the unit and store all values as integers; or (2) use the Decimal class.



Secondly, the repetition in the code can be avoided. The currencies can be stored and iterated over one by one in a for loop. A simple approach to store all kinds of currencies is to use a list as others have shown. Another option is to use enumerations.



Thirdly, the logic could be simplified a bit by using the modulo operation change % denom to calculate change - change // denom * denom.



Following is refactored code:



from enum import Enum
from decimal import Decimal

class Currency(Enum):
TWENTY = "20.00", "Twenties"
TEN = "10.00", "Tens"
FIVE = "5.00", "Fives"
ONE = "1.00", "Ones"
# more ...
PENNY = "0.01", "Pennies"

def __init__(self, denomination, print_name):
self.denomination = Decimal(denomination)
self.print_name = print_name

def change_return(cost, paid):
cost = Decimal(cost)
paid = Decimal(paid)

change = paid - cost
change_dict = # Before Python 3.6 dictionaries do not preserve order so a sorting may be needed before printing

if change < 0:
return None # Better to raise an exception if performance is not a major concern

# Better to do rounding here rather than before calculating the change
# round() could also be used here, quantize() offers more rounding options, if needed
precision = Decimal("0.01")
change = change.quantize(precision)

# Note that the iteration order follows the declaration order in the Currency Enum,
# therefore currencies should be declared in descending order of their denominations
# to yield the desired outcome
for cur in Currency:
currency_cnt, change = divmod(change, cur.denomination) # divmod(a, b) returns a tuple (a // b, a % b)
if currency_cnt: # Same as currency_cnt != 0
change_dict[cur] = currency_cnt
return change_dict

if __name__ == "__main__":
changes = change_return("30", "86.13")
if changes is None:
print('Insufficient funds')
else:
for cur, count in changes.items():
print(f"cur.print_name:<9: count") # Format output using format strings





share|improve this answer












$endgroup$



Firstly, when working with monetary values, to avoid floating-point-number-related issues (you can try calling change_return(1, 1.15) in your solution to see it), it is more common to either (1) use cent as the unit and store all values as integers; or (2) use the Decimal class.



Secondly, the repetition in the code can be avoided. The currencies can be stored and iterated over one by one in a for loop. A simple approach to store all kinds of currencies is to use a list as others have shown. Another option is to use enumerations.



Thirdly, the logic could be simplified a bit by using the modulo operation change % denom to calculate change - change // denom * denom.



Following is refactored code:



from enum import Enum
from decimal import Decimal

class Currency(Enum):
TWENTY = "20.00", "Twenties"
TEN = "10.00", "Tens"
FIVE = "5.00", "Fives"
ONE = "1.00", "Ones"
# more ...
PENNY = "0.01", "Pennies"

def __init__(self, denomination, print_name):
self.denomination = Decimal(denomination)
self.print_name = print_name

def change_return(cost, paid):
cost = Decimal(cost)
paid = Decimal(paid)

change = paid - cost
change_dict = # Before Python 3.6 dictionaries do not preserve order so a sorting may be needed before printing

if change < 0:
return None # Better to raise an exception if performance is not a major concern

# Better to do rounding here rather than before calculating the change
# round() could also be used here, quantize() offers more rounding options, if needed
precision = Decimal("0.01")
change = change.quantize(precision)

# Note that the iteration order follows the declaration order in the Currency Enum,
# therefore currencies should be declared in descending order of their denominations
# to yield the desired outcome
for cur in Currency:
currency_cnt, change = divmod(change, cur.denomination) # divmod(a, b) returns a tuple (a // b, a % b)
if currency_cnt: # Same as currency_cnt != 0
change_dict[cur] = currency_cnt
return change_dict

if __name__ == "__main__":
changes = change_return("30", "86.13")
if changes is None:
print('Insufficient funds')
else:
for cur, count in changes.items():
print(f"cur.print_name:<9: count") # Format output using format strings






share|improve this answer















share|improve this answer




share|improve this answer








edited Sep 9 at 14:41

























answered Sep 8 at 8:01









GZ0GZ0

1,2491 silver badge10 bronze badges




1,2491 silver badge10 bronze badges














  • $begingroup$
    Good call out on Decimal or using ints and nice use of Enum. You could use a dict but you would have to sorted() the iterator unless you want to rely on Py3.6+ having ordered dictionaries. You don't really need the else after a return. I would be tempted to raise ValueError('Insufficient Funds') vs return None.
    $endgroup$
    – AChampion
    Sep 9 at 0:34










  • $begingroup$
    @AChampion Thanks for the comment. A few points: 1. It does rely on ordered dictionaries. A comment is added. I considered to return a list of tuples but eventually kept the dictionary just in case a lookup is needed on that. 2. I agree with you in this case but be aware that sometimes an explicit else can improve readablity. 3. I was also tempted to raise an error but eventually hold it back because of the potential performance overhead of handling exceptions and not knowing the full requirement.
    $endgroup$
    – GZ0
    Sep 9 at 1:23










  • $begingroup$
    I'm loving the divmod use. Perhaps add a note about the fact that the iteration order of for cur in Currencyis dependent on the declaration order in the Currency Enum? This is a gotcha in progressing code development here.
    $endgroup$
    – Gloweye
    Sep 9 at 14:25










  • $begingroup$
    @JaccovanDorp Thanks. A comment about that is added.
    $endgroup$
    – GZ0
    Sep 9 at 14:42

















  • $begingroup$
    Good call out on Decimal or using ints and nice use of Enum. You could use a dict but you would have to sorted() the iterator unless you want to rely on Py3.6+ having ordered dictionaries. You don't really need the else after a return. I would be tempted to raise ValueError('Insufficient Funds') vs return None.
    $endgroup$
    – AChampion
    Sep 9 at 0:34










  • $begingroup$
    @AChampion Thanks for the comment. A few points: 1. It does rely on ordered dictionaries. A comment is added. I considered to return a list of tuples but eventually kept the dictionary just in case a lookup is needed on that. 2. I agree with you in this case but be aware that sometimes an explicit else can improve readablity. 3. I was also tempted to raise an error but eventually hold it back because of the potential performance overhead of handling exceptions and not knowing the full requirement.
    $endgroup$
    – GZ0
    Sep 9 at 1:23










  • $begingroup$
    I'm loving the divmod use. Perhaps add a note about the fact that the iteration order of for cur in Currencyis dependent on the declaration order in the Currency Enum? This is a gotcha in progressing code development here.
    $endgroup$
    – Gloweye
    Sep 9 at 14:25










  • $begingroup$
    @JaccovanDorp Thanks. A comment about that is added.
    $endgroup$
    – GZ0
    Sep 9 at 14:42
















$begingroup$
Good call out on Decimal or using ints and nice use of Enum. You could use a dict but you would have to sorted() the iterator unless you want to rely on Py3.6+ having ordered dictionaries. You don't really need the else after a return. I would be tempted to raise ValueError('Insufficient Funds') vs return None.
$endgroup$
– AChampion
Sep 9 at 0:34




$begingroup$
Good call out on Decimal or using ints and nice use of Enum. You could use a dict but you would have to sorted() the iterator unless you want to rely on Py3.6+ having ordered dictionaries. You don't really need the else after a return. I would be tempted to raise ValueError('Insufficient Funds') vs return None.
$endgroup$
– AChampion
Sep 9 at 0:34












$begingroup$
@AChampion Thanks for the comment. A few points: 1. It does rely on ordered dictionaries. A comment is added. I considered to return a list of tuples but eventually kept the dictionary just in case a lookup is needed on that. 2. I agree with you in this case but be aware that sometimes an explicit else can improve readablity. 3. I was also tempted to raise an error but eventually hold it back because of the potential performance overhead of handling exceptions and not knowing the full requirement.
$endgroup$
– GZ0
Sep 9 at 1:23




$begingroup$
@AChampion Thanks for the comment. A few points: 1. It does rely on ordered dictionaries. A comment is added. I considered to return a list of tuples but eventually kept the dictionary just in case a lookup is needed on that. 2. I agree with you in this case but be aware that sometimes an explicit else can improve readablity. 3. I was also tempted to raise an error but eventually hold it back because of the potential performance overhead of handling exceptions and not knowing the full requirement.
$endgroup$
– GZ0
Sep 9 at 1:23












$begingroup$
I'm loving the divmod use. Perhaps add a note about the fact that the iteration order of for cur in Currencyis dependent on the declaration order in the Currency Enum? This is a gotcha in progressing code development here.
$endgroup$
– Gloweye
Sep 9 at 14:25




$begingroup$
I'm loving the divmod use. Perhaps add a note about the fact that the iteration order of for cur in Currencyis dependent on the declaration order in the Currency Enum? This is a gotcha in progressing code development here.
$endgroup$
– Gloweye
Sep 9 at 14:25












$begingroup$
@JaccovanDorp Thanks. A comment about that is added.
$endgroup$
– GZ0
Sep 9 at 14:42





$begingroup$
@JaccovanDorp Thanks. A comment about that is added.
$endgroup$
– GZ0
Sep 9 at 14:42














6

















$begingroup$

Style



I suggest you check PEP0008 https://www.python.org/dev/peps/pep-0008/ the official Python style guide to give you some insights on how to write a Pythonic style code.



def change_return(cost,paid):



  • Docstrings: Python Docstring is the documentation string which is string literal, and it occurs in the class, module, function or method definition, and it is written as a first statement. Docstrings are accessible from the doc attribute for any of the Python object and also with the built-in help() function can come in handy. You should include a docstrings to your functions explaining what they do and what they return:



    def calculate_total_change(cost, paid):
    """Calculate change and return total"""
    # code



  • f-Strings PEP 498 introduced a new string formatting mechanism known as Literal String Interpolation or more commonly as F-strings (because of the leading f character preceding the string literal). ... In Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces and this facilitates the insertion of variables in strings.



    print(key + ': ' + str(value))


    can be written:



    print(f'key: value')


  • Blank lines: Too much blank lines in your function:


PEP008: Surround top-level function and class definitions with two blank lines.
Method definitions inside a class are surrounded by a single blank line.
Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).
Use blank lines in functions, sparingly, to indicate logical sections.



Code




  • A function should return a function usually returns something instead of printing.



    else:
    print('Insufficient funds')

    for key,value in change_dict.items():
    if value > 0:
    print(key + ': ' + str(value))


You might do the following instead:



return change_dict


Then use if __name__ == '__main__': guard at the end of your script which allows other modules to import your module without running the whole script like this:



if __name__ == '__main__': 
change = change_return(15, 20)
if change:
for unit, value in change.items():
print(f'value: unit')
if not change:
print('Amount paid is exact.')


Here's a refactored version of your code:



from fractions import Fraction


def calculate_total_change(cost, paid):
"""
cost: a float/int representing the total cost.
paid: a float/int representing amount paid
return: Total change (dictionary).
"""
units = [('Twenties', 20), ('Tens', 10), ('Fives', 5), ('Ones', 1), ('Quarters', Fraction(1 / 4)),
('Dimes', Fraction(1 / 10)), ('Nickels', Fraction(5 / 100)), ('Pennies', Fraction(1 / 100))]
total_change = unit[0]: 0 for unit in units
if cost > paid:
raise ValueError(f'Insufficient amount paid for cost cost.')
if cost == paid:
print('No change.')
return
if cost < paid:
change = paid - cost
if change:
for unit in units:
while change - unit[1] >= 0:
total_change[unit[0]] += 1
change -= unit[1]
return total_change


if __name__ == '__main__':
change = calculate_total_change(15, 22.5)
if change:
for unit, value in change.items():
print(f'unit: value')
else:
print('Amount exact')





share|improve this answer












$endgroup$










  • 1




    $begingroup$
    Is there a reason you use Fraction over Decimal? Also, your return value is inconsistent - if someone pays the exact amount you return an integer instead of a dict, which will break your loop in if __name__ == "__main__". I think it would be more consistent to return an empty dict there.
    $endgroup$
    – Gloweye
    Sep 10 at 6:48










  • $begingroup$
    thanks for pointing this out, I'll edit the code and there is no specific reason for choosing Fraction over Decimal, I guess both would do the same job, I'm more familiar with Fraction that's all and why returning a zero will break the loop?
    $endgroup$
    – user203258
    Sep 10 at 6:52











  • $begingroup$
    I think returning a zero would be inconsistent with what is indicated in the docstring however, it won't break the loop because both a zero and the empty dict have the same boolean value (False)
    $endgroup$
    – user203258
    Sep 10 at 7:00










  • $begingroup$
    Oh, right, didn't see that if change:. However, perhaps someone will use the function somewhere else - and having to check it would be a pain in that case.
    $endgroup$
    – Gloweye
    Sep 10 at 7:14















6

















$begingroup$

Style



I suggest you check PEP0008 https://www.python.org/dev/peps/pep-0008/ the official Python style guide to give you some insights on how to write a Pythonic style code.



def change_return(cost,paid):



  • Docstrings: Python Docstring is the documentation string which is string literal, and it occurs in the class, module, function or method definition, and it is written as a first statement. Docstrings are accessible from the doc attribute for any of the Python object and also with the built-in help() function can come in handy. You should include a docstrings to your functions explaining what they do and what they return:



    def calculate_total_change(cost, paid):
    """Calculate change and return total"""
    # code



  • f-Strings PEP 498 introduced a new string formatting mechanism known as Literal String Interpolation or more commonly as F-strings (because of the leading f character preceding the string literal). ... In Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces and this facilitates the insertion of variables in strings.



    print(key + ': ' + str(value))


    can be written:



    print(f'key: value')


  • Blank lines: Too much blank lines in your function:


PEP008: Surround top-level function and class definitions with two blank lines.
Method definitions inside a class are surrounded by a single blank line.
Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).
Use blank lines in functions, sparingly, to indicate logical sections.



Code




  • A function should return a function usually returns something instead of printing.



    else:
    print('Insufficient funds')

    for key,value in change_dict.items():
    if value > 0:
    print(key + ': ' + str(value))


You might do the following instead:



return change_dict


Then use if __name__ == '__main__': guard at the end of your script which allows other modules to import your module without running the whole script like this:



if __name__ == '__main__': 
change = change_return(15, 20)
if change:
for unit, value in change.items():
print(f'value: unit')
if not change:
print('Amount paid is exact.')


Here's a refactored version of your code:



from fractions import Fraction


def calculate_total_change(cost, paid):
"""
cost: a float/int representing the total cost.
paid: a float/int representing amount paid
return: Total change (dictionary).
"""
units = [('Twenties', 20), ('Tens', 10), ('Fives', 5), ('Ones', 1), ('Quarters', Fraction(1 / 4)),
('Dimes', Fraction(1 / 10)), ('Nickels', Fraction(5 / 100)), ('Pennies', Fraction(1 / 100))]
total_change = unit[0]: 0 for unit in units
if cost > paid:
raise ValueError(f'Insufficient amount paid for cost cost.')
if cost == paid:
print('No change.')
return
if cost < paid:
change = paid - cost
if change:
for unit in units:
while change - unit[1] >= 0:
total_change[unit[0]] += 1
change -= unit[1]
return total_change


if __name__ == '__main__':
change = calculate_total_change(15, 22.5)
if change:
for unit, value in change.items():
print(f'unit: value')
else:
print('Amount exact')





share|improve this answer












$endgroup$










  • 1




    $begingroup$
    Is there a reason you use Fraction over Decimal? Also, your return value is inconsistent - if someone pays the exact amount you return an integer instead of a dict, which will break your loop in if __name__ == "__main__". I think it would be more consistent to return an empty dict there.
    $endgroup$
    – Gloweye
    Sep 10 at 6:48










  • $begingroup$
    thanks for pointing this out, I'll edit the code and there is no specific reason for choosing Fraction over Decimal, I guess both would do the same job, I'm more familiar with Fraction that's all and why returning a zero will break the loop?
    $endgroup$
    – user203258
    Sep 10 at 6:52











  • $begingroup$
    I think returning a zero would be inconsistent with what is indicated in the docstring however, it won't break the loop because both a zero and the empty dict have the same boolean value (False)
    $endgroup$
    – user203258
    Sep 10 at 7:00










  • $begingroup$
    Oh, right, didn't see that if change:. However, perhaps someone will use the function somewhere else - and having to check it would be a pain in that case.
    $endgroup$
    – Gloweye
    Sep 10 at 7:14













6















6











6







$begingroup$

Style



I suggest you check PEP0008 https://www.python.org/dev/peps/pep-0008/ the official Python style guide to give you some insights on how to write a Pythonic style code.



def change_return(cost,paid):



  • Docstrings: Python Docstring is the documentation string which is string literal, and it occurs in the class, module, function or method definition, and it is written as a first statement. Docstrings are accessible from the doc attribute for any of the Python object and also with the built-in help() function can come in handy. You should include a docstrings to your functions explaining what they do and what they return:



    def calculate_total_change(cost, paid):
    """Calculate change and return total"""
    # code



  • f-Strings PEP 498 introduced a new string formatting mechanism known as Literal String Interpolation or more commonly as F-strings (because of the leading f character preceding the string literal). ... In Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces and this facilitates the insertion of variables in strings.



    print(key + ': ' + str(value))


    can be written:



    print(f'key: value')


  • Blank lines: Too much blank lines in your function:


PEP008: Surround top-level function and class definitions with two blank lines.
Method definitions inside a class are surrounded by a single blank line.
Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).
Use blank lines in functions, sparingly, to indicate logical sections.



Code




  • A function should return a function usually returns something instead of printing.



    else:
    print('Insufficient funds')

    for key,value in change_dict.items():
    if value > 0:
    print(key + ': ' + str(value))


You might do the following instead:



return change_dict


Then use if __name__ == '__main__': guard at the end of your script which allows other modules to import your module without running the whole script like this:



if __name__ == '__main__': 
change = change_return(15, 20)
if change:
for unit, value in change.items():
print(f'value: unit')
if not change:
print('Amount paid is exact.')


Here's a refactored version of your code:



from fractions import Fraction


def calculate_total_change(cost, paid):
"""
cost: a float/int representing the total cost.
paid: a float/int representing amount paid
return: Total change (dictionary).
"""
units = [('Twenties', 20), ('Tens', 10), ('Fives', 5), ('Ones', 1), ('Quarters', Fraction(1 / 4)),
('Dimes', Fraction(1 / 10)), ('Nickels', Fraction(5 / 100)), ('Pennies', Fraction(1 / 100))]
total_change = unit[0]: 0 for unit in units
if cost > paid:
raise ValueError(f'Insufficient amount paid for cost cost.')
if cost == paid:
print('No change.')
return
if cost < paid:
change = paid - cost
if change:
for unit in units:
while change - unit[1] >= 0:
total_change[unit[0]] += 1
change -= unit[1]
return total_change


if __name__ == '__main__':
change = calculate_total_change(15, 22.5)
if change:
for unit, value in change.items():
print(f'unit: value')
else:
print('Amount exact')





share|improve this answer












$endgroup$



Style



I suggest you check PEP0008 https://www.python.org/dev/peps/pep-0008/ the official Python style guide to give you some insights on how to write a Pythonic style code.



def change_return(cost,paid):



  • Docstrings: Python Docstring is the documentation string which is string literal, and it occurs in the class, module, function or method definition, and it is written as a first statement. Docstrings are accessible from the doc attribute for any of the Python object and also with the built-in help() function can come in handy. You should include a docstrings to your functions explaining what they do and what they return:



    def calculate_total_change(cost, paid):
    """Calculate change and return total"""
    # code



  • f-Strings PEP 498 introduced a new string formatting mechanism known as Literal String Interpolation or more commonly as F-strings (because of the leading f character preceding the string literal). ... In Python source code, an f-string is a literal string, prefixed with 'f', which contains expressions inside braces and this facilitates the insertion of variables in strings.



    print(key + ': ' + str(value))


    can be written:



    print(f'key: value')


  • Blank lines: Too much blank lines in your function:


PEP008: Surround top-level function and class definitions with two blank lines.
Method definitions inside a class are surrounded by a single blank line.
Extra blank lines may be used (sparingly) to separate groups of related functions. Blank lines may be omitted between a bunch of related one-liners (e.g. a set of dummy implementations).
Use blank lines in functions, sparingly, to indicate logical sections.



Code




  • A function should return a function usually returns something instead of printing.



    else:
    print('Insufficient funds')

    for key,value in change_dict.items():
    if value > 0:
    print(key + ': ' + str(value))


You might do the following instead:



return change_dict


Then use if __name__ == '__main__': guard at the end of your script which allows other modules to import your module without running the whole script like this:



if __name__ == '__main__': 
change = change_return(15, 20)
if change:
for unit, value in change.items():
print(f'value: unit')
if not change:
print('Amount paid is exact.')


Here's a refactored version of your code:



from fractions import Fraction


def calculate_total_change(cost, paid):
"""
cost: a float/int representing the total cost.
paid: a float/int representing amount paid
return: Total change (dictionary).
"""
units = [('Twenties', 20), ('Tens', 10), ('Fives', 5), ('Ones', 1), ('Quarters', Fraction(1 / 4)),
('Dimes', Fraction(1 / 10)), ('Nickels', Fraction(5 / 100)), ('Pennies', Fraction(1 / 100))]
total_change = unit[0]: 0 for unit in units
if cost > paid:
raise ValueError(f'Insufficient amount paid for cost cost.')
if cost == paid:
print('No change.')
return
if cost < paid:
change = paid - cost
if change:
for unit in units:
while change - unit[1] >= 0:
total_change[unit[0]] += 1
change -= unit[1]
return total_change


if __name__ == '__main__':
change = calculate_total_change(15, 22.5)
if change:
for unit, value in change.items():
print(f'unit: value')
else:
print('Amount exact')






share|improve this answer















share|improve this answer




share|improve this answer








edited Sep 10 at 6:53

























answered Sep 8 at 3:29







user203258user203258

















  • 1




    $begingroup$
    Is there a reason you use Fraction over Decimal? Also, your return value is inconsistent - if someone pays the exact amount you return an integer instead of a dict, which will break your loop in if __name__ == "__main__". I think it would be more consistent to return an empty dict there.
    $endgroup$
    – Gloweye
    Sep 10 at 6:48










  • $begingroup$
    thanks for pointing this out, I'll edit the code and there is no specific reason for choosing Fraction over Decimal, I guess both would do the same job, I'm more familiar with Fraction that's all and why returning a zero will break the loop?
    $endgroup$
    – user203258
    Sep 10 at 6:52











  • $begingroup$
    I think returning a zero would be inconsistent with what is indicated in the docstring however, it won't break the loop because both a zero and the empty dict have the same boolean value (False)
    $endgroup$
    – user203258
    Sep 10 at 7:00










  • $begingroup$
    Oh, right, didn't see that if change:. However, perhaps someone will use the function somewhere else - and having to check it would be a pain in that case.
    $endgroup$
    – Gloweye
    Sep 10 at 7:14












  • 1




    $begingroup$
    Is there a reason you use Fraction over Decimal? Also, your return value is inconsistent - if someone pays the exact amount you return an integer instead of a dict, which will break your loop in if __name__ == "__main__". I think it would be more consistent to return an empty dict there.
    $endgroup$
    – Gloweye
    Sep 10 at 6:48










  • $begingroup$
    thanks for pointing this out, I'll edit the code and there is no specific reason for choosing Fraction over Decimal, I guess both would do the same job, I'm more familiar with Fraction that's all and why returning a zero will break the loop?
    $endgroup$
    – user203258
    Sep 10 at 6:52











  • $begingroup$
    I think returning a zero would be inconsistent with what is indicated in the docstring however, it won't break the loop because both a zero and the empty dict have the same boolean value (False)
    $endgroup$
    – user203258
    Sep 10 at 7:00










  • $begingroup$
    Oh, right, didn't see that if change:. However, perhaps someone will use the function somewhere else - and having to check it would be a pain in that case.
    $endgroup$
    – Gloweye
    Sep 10 at 7:14







1




1




$begingroup$
Is there a reason you use Fraction over Decimal? Also, your return value is inconsistent - if someone pays the exact amount you return an integer instead of a dict, which will break your loop in if __name__ == "__main__". I think it would be more consistent to return an empty dict there.
$endgroup$
– Gloweye
Sep 10 at 6:48




$begingroup$
Is there a reason you use Fraction over Decimal? Also, your return value is inconsistent - if someone pays the exact amount you return an integer instead of a dict, which will break your loop in if __name__ == "__main__". I think it would be more consistent to return an empty dict there.
$endgroup$
– Gloweye
Sep 10 at 6:48












$begingroup$
thanks for pointing this out, I'll edit the code and there is no specific reason for choosing Fraction over Decimal, I guess both would do the same job, I'm more familiar with Fraction that's all and why returning a zero will break the loop?
$endgroup$
– user203258
Sep 10 at 6:52





$begingroup$
thanks for pointing this out, I'll edit the code and there is no specific reason for choosing Fraction over Decimal, I guess both would do the same job, I'm more familiar with Fraction that's all and why returning a zero will break the loop?
$endgroup$
– user203258
Sep 10 at 6:52













$begingroup$
I think returning a zero would be inconsistent with what is indicated in the docstring however, it won't break the loop because both a zero and the empty dict have the same boolean value (False)
$endgroup$
– user203258
Sep 10 at 7:00




$begingroup$
I think returning a zero would be inconsistent with what is indicated in the docstring however, it won't break the loop because both a zero and the empty dict have the same boolean value (False)
$endgroup$
– user203258
Sep 10 at 7:00












$begingroup$
Oh, right, didn't see that if change:. However, perhaps someone will use the function somewhere else - and having to check it would be a pain in that case.
$endgroup$
– Gloweye
Sep 10 at 7:14




$begingroup$
Oh, right, didn't see that if change:. However, perhaps someone will use the function somewhere else - and having to check it would be a pain in that case.
$endgroup$
– Gloweye
Sep 10 at 7:14











5

















$begingroup$

I would clean it up by separating my declarations from my code. With the below code, it becomes immediately clear to the reader that you are working with the various denominations of US currency. I have to work to figure it out in your code. Also, this lends itself to using other currency. I could define a different denominations variable to work with different currencies without changing my code at all.



from collections import namedtuple

Denomination = namedtuple('Denomination', 'name value')
denominations = [
Denomination("Twenties", 20.00),
Denomination("Tens", 10.00),
Denomination("Fives", 5.00),
Denomination("Ones", 1.00),
Denomination("Quarters", 0.25),
Denomination("Dimes", 0.10),
Denomination("Nickles", 0.05),
Denomination("Pennies", 0.01)
]


def change_return(cost, paid):
change = round(paid-cost,2)
print(f'Change due: change')

if change > 0:
for d in denominations:
used = change // d.value
if used > 0:
print(d.name + ": " + str(d.value))
change -= d.value * used

else:
print('Insufficient funds')


if __name__ == '__main__':
change_return(30, 75.13)





share|improve this answer










$endgroup$










  • 5




    $begingroup$
    The poor person that gives exactly the right amount to the cashier/program. They are told that they have insufficient funds (it should probably be elif change < 0).
    $endgroup$
    – Graipher
    Sep 8 at 15:32















5

















$begingroup$

I would clean it up by separating my declarations from my code. With the below code, it becomes immediately clear to the reader that you are working with the various denominations of US currency. I have to work to figure it out in your code. Also, this lends itself to using other currency. I could define a different denominations variable to work with different currencies without changing my code at all.



from collections import namedtuple

Denomination = namedtuple('Denomination', 'name value')
denominations = [
Denomination("Twenties", 20.00),
Denomination("Tens", 10.00),
Denomination("Fives", 5.00),
Denomination("Ones", 1.00),
Denomination("Quarters", 0.25),
Denomination("Dimes", 0.10),
Denomination("Nickles", 0.05),
Denomination("Pennies", 0.01)
]


def change_return(cost, paid):
change = round(paid-cost,2)
print(f'Change due: change')

if change > 0:
for d in denominations:
used = change // d.value
if used > 0:
print(d.name + ": " + str(d.value))
change -= d.value * used

else:
print('Insufficient funds')


if __name__ == '__main__':
change_return(30, 75.13)





share|improve this answer










$endgroup$










  • 5




    $begingroup$
    The poor person that gives exactly the right amount to the cashier/program. They are told that they have insufficient funds (it should probably be elif change < 0).
    $endgroup$
    – Graipher
    Sep 8 at 15:32













5















5











5







$begingroup$

I would clean it up by separating my declarations from my code. With the below code, it becomes immediately clear to the reader that you are working with the various denominations of US currency. I have to work to figure it out in your code. Also, this lends itself to using other currency. I could define a different denominations variable to work with different currencies without changing my code at all.



from collections import namedtuple

Denomination = namedtuple('Denomination', 'name value')
denominations = [
Denomination("Twenties", 20.00),
Denomination("Tens", 10.00),
Denomination("Fives", 5.00),
Denomination("Ones", 1.00),
Denomination("Quarters", 0.25),
Denomination("Dimes", 0.10),
Denomination("Nickles", 0.05),
Denomination("Pennies", 0.01)
]


def change_return(cost, paid):
change = round(paid-cost,2)
print(f'Change due: change')

if change > 0:
for d in denominations:
used = change // d.value
if used > 0:
print(d.name + ": " + str(d.value))
change -= d.value * used

else:
print('Insufficient funds')


if __name__ == '__main__':
change_return(30, 75.13)





share|improve this answer










$endgroup$



I would clean it up by separating my declarations from my code. With the below code, it becomes immediately clear to the reader that you are working with the various denominations of US currency. I have to work to figure it out in your code. Also, this lends itself to using other currency. I could define a different denominations variable to work with different currencies without changing my code at all.



from collections import namedtuple

Denomination = namedtuple('Denomination', 'name value')
denominations = [
Denomination("Twenties", 20.00),
Denomination("Tens", 10.00),
Denomination("Fives", 5.00),
Denomination("Ones", 1.00),
Denomination("Quarters", 0.25),
Denomination("Dimes", 0.10),
Denomination("Nickles", 0.05),
Denomination("Pennies", 0.01)
]


def change_return(cost, paid):
change = round(paid-cost,2)
print(f'Change due: change')

if change > 0:
for d in denominations:
used = change // d.value
if used > 0:
print(d.name + ": " + str(d.value))
change -= d.value * used

else:
print('Insufficient funds')


if __name__ == '__main__':
change_return(30, 75.13)






share|improve this answer













share|improve this answer




share|improve this answer










answered Sep 8 at 3:26









SteveJSteveJ

3611 silver badge5 bronze badges




3611 silver badge5 bronze badges










  • 5




    $begingroup$
    The poor person that gives exactly the right amount to the cashier/program. They are told that they have insufficient funds (it should probably be elif change < 0).
    $endgroup$
    – Graipher
    Sep 8 at 15:32












  • 5




    $begingroup$
    The poor person that gives exactly the right amount to the cashier/program. They are told that they have insufficient funds (it should probably be elif change < 0).
    $endgroup$
    – Graipher
    Sep 8 at 15:32







5




5




$begingroup$
The poor person that gives exactly the right amount to the cashier/program. They are told that they have insufficient funds (it should probably be elif change < 0).
$endgroup$
– Graipher
Sep 8 at 15:32




$begingroup$
The poor person that gives exactly the right amount to the cashier/program. They are told that they have insufficient funds (it should probably be elif change < 0).
$endgroup$
– Graipher
Sep 8 at 15:32











5

















$begingroup$

You have a lot of repeating code (x = change // y).
To fix this, you can use the following function:




def get_denomination(change, denom):
num_of_denom = change // denom
return (num_of_denom, change - (num_of_denom * denom))
# New use-case
twenties, change = get_denomination(change, 20.00)



And, to avoid calculating the amount of every denomination in the case that change reaches zero before the end of your conditional block:




def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =
denominations = [20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01]
titles = ['Twenties', 'Tens', 'Fives', 'Ones', 'Quarters', 'Dimes',
'Nickels', 'Pennies']

for index, denomination in enumerate(denominations):
num_denom, change = get_denomination(change, denomination)
change_dict[titles[index]] = num_denom
if change == 0:
break

if change < 0:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))





share|improve this answer










$endgroup$














  • $begingroup$
    Why are you re-implementing the divmod() builtin? Also, it will be a bit better if you zip(titles, denominations) instead of enumerate + indexing.
    $endgroup$
    – Gloweye
    Sep 10 at 6:53















5

















$begingroup$

You have a lot of repeating code (x = change // y).
To fix this, you can use the following function:




def get_denomination(change, denom):
num_of_denom = change // denom
return (num_of_denom, change - (num_of_denom * denom))
# New use-case
twenties, change = get_denomination(change, 20.00)



And, to avoid calculating the amount of every denomination in the case that change reaches zero before the end of your conditional block:




def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =
denominations = [20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01]
titles = ['Twenties', 'Tens', 'Fives', 'Ones', 'Quarters', 'Dimes',
'Nickels', 'Pennies']

for index, denomination in enumerate(denominations):
num_denom, change = get_denomination(change, denomination)
change_dict[titles[index]] = num_denom
if change == 0:
break

if change < 0:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))





share|improve this answer










$endgroup$














  • $begingroup$
    Why are you re-implementing the divmod() builtin? Also, it will be a bit better if you zip(titles, denominations) instead of enumerate + indexing.
    $endgroup$
    – Gloweye
    Sep 10 at 6:53













5















5











5







$begingroup$

You have a lot of repeating code (x = change // y).
To fix this, you can use the following function:




def get_denomination(change, denom):
num_of_denom = change // denom
return (num_of_denom, change - (num_of_denom * denom))
# New use-case
twenties, change = get_denomination(change, 20.00)



And, to avoid calculating the amount of every denomination in the case that change reaches zero before the end of your conditional block:




def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =
denominations = [20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01]
titles = ['Twenties', 'Tens', 'Fives', 'Ones', 'Quarters', 'Dimes',
'Nickels', 'Pennies']

for index, denomination in enumerate(denominations):
num_denom, change = get_denomination(change, denomination)
change_dict[titles[index]] = num_denom
if change == 0:
break

if change < 0:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))





share|improve this answer










$endgroup$



You have a lot of repeating code (x = change // y).
To fix this, you can use the following function:




def get_denomination(change, denom):
num_of_denom = change // denom
return (num_of_denom, change - (num_of_denom * denom))
# New use-case
twenties, change = get_denomination(change, 20.00)



And, to avoid calculating the amount of every denomination in the case that change reaches zero before the end of your conditional block:




def change_return(cost,paid):

change = round(paid-cost,2)
print(f'Change due: change')

change_dict =
denominations = [20, 10, 5, 1, 0.25, 0.1, 0.05, 0.01]
titles = ['Twenties', 'Tens', 'Fives', 'Ones', 'Quarters', 'Dimes',
'Nickels', 'Pennies']

for index, denomination in enumerate(denominations):
num_denom, change = get_denomination(change, denomination)
change_dict[titles[index]] = num_denom
if change == 0:
break

if change < 0:
print('Insufficient funds')

for key,value in change_dict.items():
if value > 0:
print(key + ': ' + str(value))






share|improve this answer













share|improve this answer




share|improve this answer










answered Sep 8 at 3:33









ConfettimakerConfettimaker

6096 silver badges17 bronze badges




6096 silver badges17 bronze badges














  • $begingroup$
    Why are you re-implementing the divmod() builtin? Also, it will be a bit better if you zip(titles, denominations) instead of enumerate + indexing.
    $endgroup$
    – Gloweye
    Sep 10 at 6:53
















  • $begingroup$
    Why are you re-implementing the divmod() builtin? Also, it will be a bit better if you zip(titles, denominations) instead of enumerate + indexing.
    $endgroup$
    – Gloweye
    Sep 10 at 6:53















$begingroup$
Why are you re-implementing the divmod() builtin? Also, it will be a bit better if you zip(titles, denominations) instead of enumerate + indexing.
$endgroup$
– Gloweye
Sep 10 at 6:53




$begingroup$
Why are you re-implementing the divmod() builtin? Also, it will be a bit better if you zip(titles, denominations) instead of enumerate + indexing.
$endgroup$
– Gloweye
Sep 10 at 6:53


















draft saved

draft discarded















































Thanks for contributing an answer to Code Review Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

Use MathJax to format equations. MathJax reference.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f227646%2fchange-due-function%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown









Popular posts from this blog

Tamil (spriik) Luke uk diar | Nawigatjuun

Align equal signs while including text over equalitiesAMS align: left aligned text/math plus multicolumn alignmentMultiple alignmentsAligning equations in multiple placesNumbering and aligning an equation with multiple columnsHow to align one equation with another multline equationUsing \ in environments inside the begintabularxNumber equations and preserving alignment of equal signsHow can I align equations to the left and to the right?Double equation alignment problem within align enviromentAligned within align: Why are they right-aligned?

Where does the image of a data connector as a sharp metal spike originate from?Where does the concept of infected people turning into zombies only after death originate from?Where does the motif of a reanimated human head originate?Where did the notion that Dragons could speak originate?Where does the archetypal image of the 'Grey' alien come from?Where did the suffix '-Man' originate?Where does the notion of being injured or killed by an illusion originate?Where did the term “sophont” originate?Where does the trope of magic spells being driven by advanced technology originate from?Where did the term “the living impaired” originate?