多个装饰器在python 3.7中包装了一个生成器函数-所有可能的硬币组合问题

时间:2018-11-14 03:04:50

标签: python generator python-decorators

我想编写一个程序,显示所有不同硬币(5c,10c,50c等)的组合可能达到一定值(例如$ 4)的所有方式。假设,对于初学者来说,我只想将50c,$ 1和2 $考虑为可能的组合。如果我想使用蛮力,我可以编写3个嵌套的while循环,并在每次货币合计达到我想要的值(在本例中为$ 4)时返回该货币组合。但是,由于我们考虑了更多的货币选择(例如1c,5c,10c,25c,50c,$ 1,$ 2,$ 5,$ 10,$ 50,$ 100)和更大的金额(假设我希望所有组合都获得$ 400),代码变得非常冗长且难以维护。如果我想制定一种适用于任何国家和货币组合的通用算法,则不能依靠简单的循环嵌套。

鉴于此问题,我尝试使用装饰器,以便将循环嵌套在单个函数中:

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)

这是输出:

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

但是,如果我向get_combinations()函数添加多个装饰器,则会引发TypeError:

@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

所以我有两个问题:

  1. 为什么get_combinations已经显示为生成器,并且在代码的后两行中调用它时却不需要调用它?他们不应该这样吗?

    for i in get_combinations():
        print(i)
    
  2. 在这种情况下,为什么装饰器嵌套无效?预期输出应为:

    {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}
    

编辑:仅作为示例,我希望上面的代码(具有多个装饰器)与此等效,除了不嵌套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)

0 个答案:

没有答案