Multiple decorators wrapping a generator function in python 3.7 - all possible coin combinations problem
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:
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)
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
add a comment |
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:
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)
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
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
add a comment |
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:
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)
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
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:
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)
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
python generator python-decorators
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
add a comment |
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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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