函数调用和函数中的代码给出不同的结果

时间:2019-09-03 10:57:35

标签: python numpy itertools

我的程序中存在一个错误,我试图使用带有评估表达式的Pycharm调试工具对其进行调试。我碰到了我的代码无法正常工作的原因,但是却不了解这种情况如何发生。当我用一些参数调用函数marginalizedTransition时,结果为1.0,但是当我在函数内部运行代码时,将参数放在它们的位置,则得到0.00101001196095。但是,它们应该相同,因为它们是相同的代码。
player = 1world.amountOfPlayers() = 2world.amountOfPossibleActions = 5

marginalizedTransitionTest(Ps, [(3, 1), (5, 1)], [(2, 1), (5, 1)], 0, world, 1)

其中

def marginalizedTransitionTest(Ps, sis, sfs, action, world, player):
    otherPlayers = filter(lambda x: x != player, range(world.amountOfPlayers()))
    return sum(np.prod([Ps[otherPlayer][tuple(it.chain(*sis))]
                   [np.insert(actions, player, action)[otherPlayer]]
                   for otherPlayer in otherPlayers])
           for actions in it.product(range(world.amountOfPossibleActions), repeat=world.amountOfPlayers() - 1))

给出4.05514478458和

sum(np.prod([Ps[otherPlayer][tuple(it.chain(*[(3, 1), (5, 1)]))]
               [np.insert(actions, 1, 0)[otherPlayer]]
               for otherPlayer in filter(lambda x: x != 1, range(world.amountOfPlayers()))])
       for actions in it.product(range(world.amountOfPossibleActions), repeat=world.amountOfPlayers() - 1))

给出1.0。

我不知道这是否是常见问题。如果您需要有关所使用功能的更多信息,我可以提供它们,但是此帖子已经变得很难看了。

1 个答案:

答案 0 :(得分:1)

这是由于filter作为迭代器的行为。在函数中,首先创建迭代器,然后在其上运行for多次。但是,如果您尝试仅运行以下命令:

otherPlayers = filter(lambda x: x != player, range(world.amountOfPlayers()))
for player in otherPlayers:
    print(f'1: {player}')
for player in otherPlayers:
    print(f'2: {player}')

结果将是:

1: 0
1: 1

如您所见,一旦为过滤器运行了for,它就筋疲力尽,并且不会返回任何内容。
原始运行时,您没有做相同的事情,因此迭代器按预期工作。
因此,修复很简单:在for中添加过滤器:

def marginalizedTransition(Ps, sis, sfs, action, world, player):
    return sum(world.joinedTransition(sis, sfs, np.insert(actions, player, action)) *
               np.prod([Ps[otherPlayer][tuple(it.chain(*sis))][np.insert(actions, player, action)[otherPlayer]]
                        for otherPlayer in filter(lambda x: x != player, range(world.amountOfPlayers()))])
               for actions in it.product(range(world.amountOfPossibleActions), repeat=world.amountOfPlayers() - 1))

或者,正如您在聊天中指出的那样,您可以将迭代器转换为列表:

def marginalizedTransition(Ps, sis, sfs, action, world, player):
    otherPlayers = list(filter(lambda x: x != player, range(world.amountOfPlayers())))
    return sum(world.joinedTransition(sis, sfs, np.insert(actions, player, action)) *
               np.prod([Ps[otherPlayer][tuple(it.chain(*sis))][np.insert(actions, player, action)[otherPlayer]]
                        for otherPlayer in otherPlayers])
               for actions in it.product(range(world.amountOfPossibleActions), repeat=world.amountOfPlayers() - 1))

对于想要重现此问题的SO用户,我基于聊天讨论创建了这些虚拟对象,并解构了如下的理解:

import numpy as np
import itertools as it


class World:
    amountOfPossibleActions = 5

    def amountOfPlayers(self):
        return 2

    def joinedTransition(self, a, b, insert):
        return 1 if insert[0] == 3 else 0


world = World()
player = 1

Ps = {0: {(3, 1, 5, 1): [[0.055144784579474179], [0.055144784579474179], [0.055144784579474179], [0.055144784579474179],
                         [0.055144784579474179]]}}


def marginalizedTransition(Ps, sis, sfs, action, world, player):
    otherPlayers = filter(lambda x: x != player, range(world.amountOfPlayers()))
    s = 0
    for actions in it.product(range(world.amountOfPossibleActions), repeat=world.amountOfPlayers() - 1):
        w = world.joinedTransition(sis, sfs, np.insert(actions, player, action))
        a = []
        for otherPlayer in otherPlayers:
            b = np.insert(actions, player, action)[otherPlayer]
            v = Ps[otherPlayer][tuple(it.chain(*sis))][b]
            a.append(v)
        p = np.prod(a)
        s += w * p
    return s

在个人说明上: 列表理解功能很棒,它使代码真正紧凑。但是,这会使阅读和调试代码变得非常困难。下次您想嵌套理解时,请记住这一点。如上所示,在对它进行解构之后,我只能找出问题所在。