Multiple decorators wrapping a generator function in python 3.7 - all possible coin combinations problem












0















I want to write a program that shows all the possible ways a combination of different coins (5c, 10c, 50c, etc.) can amount to a certain value (for instance, $4). Let's say, for starters, I only want to consider 50c, $1 and 2$ into the possible combinations. If I want to use brute force, I could write 3 nested while loops and return that combination of currency every time they sum up to the value I want (in this case, $4). However, as we consider more currency options (for instance, 1c, 5c, 10c, 25c, 50c, $1, $2, $5, $10, $50, $100) and bigger sums (let's say I want all combinations to get $400), the code becomes very verbose and difficult to mantain. If I want to make a generic algorithm that works for any country and combination of currencies, I can't rely on simple loop nesting.



Given this problem, I've tried using decorators so I can nest the loops in a single function:



INTENDED_SUM = 400

coin_count = {
200: 0,
100: 0,
50: 0,
}

def max_range(value):
return INTENDED_SUM / value

def coin_loop(coin_value):
def decorator(func):
while coin_count[coin_value] <= max_range(coin_value):
yield from func()
coin_count[coin_value] += 1
else:
coin_count[coin_value] = 0
return decorator


@coin_loop(100)
def get_combinations():
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count

for i in get_combinations:
print(i)


This is the output:



{200: 0, 100: 4, 50: 0}


However, if I add multiple decorators to the get_combinations() function, a TypeError is raised:



@coin_loop(200)
@coin_loop(100)
@coin_loop(50)
def get_combinations():
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count

Traceback (most recent call last):
File "31-coin_sums.py", line 29, in <module>
for i in get_combinations:
File "31-coin_sums.py", line 15, in decorator
yield from func()
TypeError: 'generator' object is not callable


So I have two questions:





  1. Why does get_combinations already shows up as a generator and does not need to be called when I call it in the last 2 lines of the code? Shouldn't they be like this?



    for i in get_combinations():
    print(i)



  2. Why isn't decorator nesting working in this case? Expected output should be something like:



    {200: 2, 100: 0, 50: 0}
    {200: 1, 100: 2, 50: 0}
    {200: 1, 100: 1, 50: 2}
    {200: 1, 100: 0, 50: 4}
    {200: 0, 100: 4, 50: 0}
    {200: 0, 100: 3, 50: 2}
    {200: 0, 100: 2, 50: 4}
    {200: 0, 100: 1, 50: 6}
    {200: 0, 100: 0, 50: 8}



EDIT: just as an example, I want the code above (with multiple decorators) to be equivalent to this, except without nested whiles:



INTENDED_SUM = 400

coin_count = {
200: 0,
100: 0,
50: 0,
}

def max_range(value):
return INTENDED_SUM / value

def get_combinations():
while coin_count[200] <= max_range(200):
while coin_count[100] <= max_range(100):
while coin_count[50] <= max_range(50):
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count
coin_count[50] += 1
else:
coin_count[50] = 0
coin_count[100] += 1
else:
coin_count[100] = 0
coin_count[200] += 1
else:
coin_count[200] = 0

for i in get_combinations():
print(i)









share|improve this question

























  • I don't understand what function your multiply-decorated function serves.

    – erip
    Nov 14 '18 at 3:10











  • I'm sorry, my code is a bit confusing. Each decorator should loop through 0 and the max possible coins of a given value (50c count shouldn't go further than 8 in this case) while calling the loop on other values and also testing if the sum of the coins add up to $4 (in this example). It should yield the dictionary of coin counts everytime they add up to that value.

    – Felipe Muniz
    Nov 14 '18 at 3:15
















0















I want to write a program that shows all the possible ways a combination of different coins (5c, 10c, 50c, etc.) can amount to a certain value (for instance, $4). Let's say, for starters, I only want to consider 50c, $1 and 2$ into the possible combinations. If I want to use brute force, I could write 3 nested while loops and return that combination of currency every time they sum up to the value I want (in this case, $4). However, as we consider more currency options (for instance, 1c, 5c, 10c, 25c, 50c, $1, $2, $5, $10, $50, $100) and bigger sums (let's say I want all combinations to get $400), the code becomes very verbose and difficult to mantain. If I want to make a generic algorithm that works for any country and combination of currencies, I can't rely on simple loop nesting.



Given this problem, I've tried using decorators so I can nest the loops in a single function:



INTENDED_SUM = 400

coin_count = {
200: 0,
100: 0,
50: 0,
}

def max_range(value):
return INTENDED_SUM / value

def coin_loop(coin_value):
def decorator(func):
while coin_count[coin_value] <= max_range(coin_value):
yield from func()
coin_count[coin_value] += 1
else:
coin_count[coin_value] = 0
return decorator


@coin_loop(100)
def get_combinations():
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count

for i in get_combinations:
print(i)


This is the output:



{200: 0, 100: 4, 50: 0}


However, if I add multiple decorators to the get_combinations() function, a TypeError is raised:



@coin_loop(200)
@coin_loop(100)
@coin_loop(50)
def get_combinations():
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count

Traceback (most recent call last):
File "31-coin_sums.py", line 29, in <module>
for i in get_combinations:
File "31-coin_sums.py", line 15, in decorator
yield from func()
TypeError: 'generator' object is not callable


So I have two questions:





  1. Why does get_combinations already shows up as a generator and does not need to be called when I call it in the last 2 lines of the code? Shouldn't they be like this?



    for i in get_combinations():
    print(i)



  2. Why isn't decorator nesting working in this case? Expected output should be something like:



    {200: 2, 100: 0, 50: 0}
    {200: 1, 100: 2, 50: 0}
    {200: 1, 100: 1, 50: 2}
    {200: 1, 100: 0, 50: 4}
    {200: 0, 100: 4, 50: 0}
    {200: 0, 100: 3, 50: 2}
    {200: 0, 100: 2, 50: 4}
    {200: 0, 100: 1, 50: 6}
    {200: 0, 100: 0, 50: 8}



EDIT: just as an example, I want the code above (with multiple decorators) to be equivalent to this, except without nested whiles:



INTENDED_SUM = 400

coin_count = {
200: 0,
100: 0,
50: 0,
}

def max_range(value):
return INTENDED_SUM / value

def get_combinations():
while coin_count[200] <= max_range(200):
while coin_count[100] <= max_range(100):
while coin_count[50] <= max_range(50):
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count
coin_count[50] += 1
else:
coin_count[50] = 0
coin_count[100] += 1
else:
coin_count[100] = 0
coin_count[200] += 1
else:
coin_count[200] = 0

for i in get_combinations():
print(i)









share|improve this question

























  • I don't understand what function your multiply-decorated function serves.

    – erip
    Nov 14 '18 at 3:10











  • I'm sorry, my code is a bit confusing. Each decorator should loop through 0 and the max possible coins of a given value (50c count shouldn't go further than 8 in this case) while calling the loop on other values and also testing if the sum of the coins add up to $4 (in this example). It should yield the dictionary of coin counts everytime they add up to that value.

    – Felipe Muniz
    Nov 14 '18 at 3:15














0












0








0








I want to write a program that shows all the possible ways a combination of different coins (5c, 10c, 50c, etc.) can amount to a certain value (for instance, $4). Let's say, for starters, I only want to consider 50c, $1 and 2$ into the possible combinations. If I want to use brute force, I could write 3 nested while loops and return that combination of currency every time they sum up to the value I want (in this case, $4). However, as we consider more currency options (for instance, 1c, 5c, 10c, 25c, 50c, $1, $2, $5, $10, $50, $100) and bigger sums (let's say I want all combinations to get $400), the code becomes very verbose and difficult to mantain. If I want to make a generic algorithm that works for any country and combination of currencies, I can't rely on simple loop nesting.



Given this problem, I've tried using decorators so I can nest the loops in a single function:



INTENDED_SUM = 400

coin_count = {
200: 0,
100: 0,
50: 0,
}

def max_range(value):
return INTENDED_SUM / value

def coin_loop(coin_value):
def decorator(func):
while coin_count[coin_value] <= max_range(coin_value):
yield from func()
coin_count[coin_value] += 1
else:
coin_count[coin_value] = 0
return decorator


@coin_loop(100)
def get_combinations():
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count

for i in get_combinations:
print(i)


This is the output:



{200: 0, 100: 4, 50: 0}


However, if I add multiple decorators to the get_combinations() function, a TypeError is raised:



@coin_loop(200)
@coin_loop(100)
@coin_loop(50)
def get_combinations():
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count

Traceback (most recent call last):
File "31-coin_sums.py", line 29, in <module>
for i in get_combinations:
File "31-coin_sums.py", line 15, in decorator
yield from func()
TypeError: 'generator' object is not callable


So I have two questions:





  1. Why does get_combinations already shows up as a generator and does not need to be called when I call it in the last 2 lines of the code? Shouldn't they be like this?



    for i in get_combinations():
    print(i)



  2. Why isn't decorator nesting working in this case? Expected output should be something like:



    {200: 2, 100: 0, 50: 0}
    {200: 1, 100: 2, 50: 0}
    {200: 1, 100: 1, 50: 2}
    {200: 1, 100: 0, 50: 4}
    {200: 0, 100: 4, 50: 0}
    {200: 0, 100: 3, 50: 2}
    {200: 0, 100: 2, 50: 4}
    {200: 0, 100: 1, 50: 6}
    {200: 0, 100: 0, 50: 8}



EDIT: just as an example, I want the code above (with multiple decorators) to be equivalent to this, except without nested whiles:



INTENDED_SUM = 400

coin_count = {
200: 0,
100: 0,
50: 0,
}

def max_range(value):
return INTENDED_SUM / value

def get_combinations():
while coin_count[200] <= max_range(200):
while coin_count[100] <= max_range(100):
while coin_count[50] <= max_range(50):
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count
coin_count[50] += 1
else:
coin_count[50] = 0
coin_count[100] += 1
else:
coin_count[100] = 0
coin_count[200] += 1
else:
coin_count[200] = 0

for i in get_combinations():
print(i)









share|improve this question
















I want to write a program that shows all the possible ways a combination of different coins (5c, 10c, 50c, etc.) can amount to a certain value (for instance, $4). Let's say, for starters, I only want to consider 50c, $1 and 2$ into the possible combinations. If I want to use brute force, I could write 3 nested while loops and return that combination of currency every time they sum up to the value I want (in this case, $4). However, as we consider more currency options (for instance, 1c, 5c, 10c, 25c, 50c, $1, $2, $5, $10, $50, $100) and bigger sums (let's say I want all combinations to get $400), the code becomes very verbose and difficult to mantain. If I want to make a generic algorithm that works for any country and combination of currencies, I can't rely on simple loop nesting.



Given this problem, I've tried using decorators so I can nest the loops in a single function:



INTENDED_SUM = 400

coin_count = {
200: 0,
100: 0,
50: 0,
}

def max_range(value):
return INTENDED_SUM / value

def coin_loop(coin_value):
def decorator(func):
while coin_count[coin_value] <= max_range(coin_value):
yield from func()
coin_count[coin_value] += 1
else:
coin_count[coin_value] = 0
return decorator


@coin_loop(100)
def get_combinations():
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count

for i in get_combinations:
print(i)


This is the output:



{200: 0, 100: 4, 50: 0}


However, if I add multiple decorators to the get_combinations() function, a TypeError is raised:



@coin_loop(200)
@coin_loop(100)
@coin_loop(50)
def get_combinations():
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count

Traceback (most recent call last):
File "31-coin_sums.py", line 29, in <module>
for i in get_combinations:
File "31-coin_sums.py", line 15, in decorator
yield from func()
TypeError: 'generator' object is not callable


So I have two questions:





  1. Why does get_combinations already shows up as a generator and does not need to be called when I call it in the last 2 lines of the code? Shouldn't they be like this?



    for i in get_combinations():
    print(i)



  2. Why isn't decorator nesting working in this case? Expected output should be something like:



    {200: 2, 100: 0, 50: 0}
    {200: 1, 100: 2, 50: 0}
    {200: 1, 100: 1, 50: 2}
    {200: 1, 100: 0, 50: 4}
    {200: 0, 100: 4, 50: 0}
    {200: 0, 100: 3, 50: 2}
    {200: 0, 100: 2, 50: 4}
    {200: 0, 100: 1, 50: 6}
    {200: 0, 100: 0, 50: 8}



EDIT: just as an example, I want the code above (with multiple decorators) to be equivalent to this, except without nested whiles:



INTENDED_SUM = 400

coin_count = {
200: 0,
100: 0,
50: 0,
}

def max_range(value):
return INTENDED_SUM / value

def get_combinations():
while coin_count[200] <= max_range(200):
while coin_count[100] <= max_range(100):
while coin_count[50] <= max_range(50):
agg = sum([i * coin_count[i] for i in coin_count])
if agg == INTENDED_SUM:
yield coin_count
coin_count[50] += 1
else:
coin_count[50] = 0
coin_count[100] += 1
else:
coin_count[100] = 0
coin_count[200] += 1
else:
coin_count[200] = 0

for i in get_combinations():
print(i)






python generator python-decorators






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 14 '18 at 3:10







Felipe Muniz

















asked Nov 14 '18 at 3:04









Felipe MunizFelipe Muniz

417




417













  • I don't understand what function your multiply-decorated function serves.

    – erip
    Nov 14 '18 at 3:10











  • I'm sorry, my code is a bit confusing. Each decorator should loop through 0 and the max possible coins of a given value (50c count shouldn't go further than 8 in this case) while calling the loop on other values and also testing if the sum of the coins add up to $4 (in this example). It should yield the dictionary of coin counts everytime they add up to that value.

    – Felipe Muniz
    Nov 14 '18 at 3:15



















  • I don't understand what function your multiply-decorated function serves.

    – erip
    Nov 14 '18 at 3:10











  • I'm sorry, my code is a bit confusing. Each decorator should loop through 0 and the max possible coins of a given value (50c count shouldn't go further than 8 in this case) while calling the loop on other values and also testing if the sum of the coins add up to $4 (in this example). It should yield the dictionary of coin counts everytime they add up to that value.

    – Felipe Muniz
    Nov 14 '18 at 3:15

















I don't understand what function your multiply-decorated function serves.

– erip
Nov 14 '18 at 3:10





I don't understand what function your multiply-decorated function serves.

– erip
Nov 14 '18 at 3:10













I'm sorry, my code is a bit confusing. Each decorator should loop through 0 and the max possible coins of a given value (50c count shouldn't go further than 8 in this case) while calling the loop on other values and also testing if the sum of the coins add up to $4 (in this example). It should yield the dictionary of coin counts everytime they add up to that value.

– Felipe Muniz
Nov 14 '18 at 3:15





I'm sorry, my code is a bit confusing. Each decorator should loop through 0 and the max possible coins of a given value (50c count shouldn't go further than 8 in this case) while calling the loop on other values and also testing if the sum of the coins add up to $4 (in this example). It should yield the dictionary of coin counts everytime they add up to that value.

– Felipe Muniz
Nov 14 '18 at 3:15












0






active

oldest

votes











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: "1"
};
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: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
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/3.0/"u003ecc by-sa 3.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%2fstackoverflow.com%2fquestions%2f53292603%2fmultiple-decorators-wrapping-a-generator-function-in-python-3-7-all-possible-c%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























0






active

oldest

votes








0






active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • 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.


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%2fstackoverflow.com%2fquestions%2f53292603%2fmultiple-decorators-wrapping-a-generator-function-in-python-3-7-all-possible-c%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

List item for chat from Array inside array React Native

Thiostrepton

Caerphilly