为什么我的函数中的这个方法会被调用,直到包含它的列表超出范围?

huangapple go评论80阅读模式
英文:

Why is this method in my function being called until the list in which it is contained in goes out of range?

问题

我有一个对象,它是一个嵌套的列表。
它用于以二维矩阵的格式保存对象的列表。
有点像这样:

[[[], [], [], ],
[[], [], [], ]]

让我们称之为"World"。
"World"是一个在程序启动时实例化的类,用于创建类似上面的空列表。
实例化对象的引用保存在一个名为"singleton"的单独方法中。

可以使用"world"中定义的"add"方法将对象添加到"World"中。

def add(self, entity, posx, posy):
    print(posx, posy)
    self.world[posx][posy].append(entity)

"entity"是要添加的对象。"posx"和"posy"确定对象放入的列表。

这些对象应该有一个在一定时间间隔后反复调用的方法。

为了遍历"World",我使用了这段代码:

def apply_entity_logic_all():
    for i in range(len(singleton.World.world)):
        for j in range(len(singleton.World.world[i])):
            for id, entity in enumerate(singleton.World.world[i][j]):
                entity.act(i, j, id)

在"entity.act"中,我只想将问题对象移动到"World"中的另一个列表中。
有点像将其移动到网格中的另一个单元格。
为此,我尝试将对象复制到新单元格中并删除之前的对象。
像这样:

def act(self, i, j, id):
    new_object = Plankton(
        posx=i+1, posy=j)
    del singleton.World.world[i][j][id]
    singleton.World.add(new_object, i+1, j)

"Plankton"是由"Entity"继承的一个类。在这种情况下,它不会影响问题行为。

问题在于"new_object"会被创建并添加到"World"中,直到越界。
它应该只被调用一次。我之所以这么说是因为在另一个类似的函数中它是有效的。只有"entity.act()"行不同,它会删除该实体。

我推测问题出在我的"act()"函数上。但我无法确定问题出在何处。
提前感谢您的帮助。

我尝试使用Python的"deepcopy()"而不是创建新的对象。但没有改变任何东西。
当我在pdb中加载它时,似乎"act()"被触发,出于我无法解释的原因,创建新对象的行会被多次调用,直到列表中的空间用尽。

编辑:经过进一步测试,我确定只有这一行

singleton.World.add(new_object, i+1, j)

被多次调用。其他行只调用一次。之前没有循环。我不知道为什么会这样。

英文:

I have an object which is a list of list of list.
It is used to hold a list of object in 2d matrix like format.
Kinda like this:

[[ [], [], [], ],
[ [], [], [], ]]

Let's call this World.
World is a class that is instantiated on startup of program to create a blank list like above.
The reference to the instanciated object is saved in a separate method called singleton.

An object can be added to the world using add method defined in world.

def add(self, entity, posx, posy):
    print(posx, posy)
    self.world[posx][posy].append(entity)

entity is the object to be added. And posx, posy determines on which list the object goes in.

The objects are supposed to have a method that is called repeatedly after a set interval.

To iterate over the World I used this code:

def apply_entity_logic_all():
    for i in range(len(singleton.World.world)):
        for j in range(len(singleton.World.world[i])):
            for id, entity in enumerate(singleton.World.world[i][j]):
                entity.act(i, j, id)

In entity.act all I want is to move the object in question to another list in World.
Sort of like moving it to another cell in the grid.
To do that I tried to copy the object over to the new cell and delete the previous one.
Like this:

def act(self, i, j, id):
    new_object = Plankton(
        posx=i+1, posy=j)
        del singleton.World.world[i][j][id]
        singleton.World.add(new_object, i+1, j)

Plankton is a class inherited by Entity. In this case it does not affect the problematic behaviour.

The problem here is new_object is being created and added to the world until it goes out of bound.
It is supposed to be called only once. I can say that because in another similar function it works. Only entity.act() line is different and it deletes the said entity instead.

I presume the problem is with my act() function. But I cannot identify what is the problem.
Thanks in advance.

I tried using python's deepcopy() instead of creating it new. It didn't change anything.
When I loaded it up in pdb it seems that when act() is triggered for reasons I cannot explain the creating of new object lines gets called until the space in list runs out.

Edit: After further testing I determined that only this line
singleton.World.add(new_object, i+1, j)
is being called multiple times. Others are called only once.
There is no loop before there. I have no idea why is that happening.

答案1

得分: 0

问题出在你在迭代列表时如何修改它。当你使用del singleton.World.world[i][j][id]来删除列表中的元素时,你正在改变当前正在迭代的列表,这可能导致意外的行为。

在Python中,for循环不会动态更新正在迭代的列表。所以,当你删除正在迭代的列表中的一个对象时,你实际上将所有后续的项目向下移动一个索引。然后,通过for id, entity in enumerate(singleton.World.world[i][j])移动到下一个索引时,你会跳过一个实体,因为一切都向下移动了一个索引。

此外,你在act()方法中创建一个新实体,然后立即将其添加到当前正在迭代的同一个列表中。这可能会创建一种循环,你正在迭代正在进行的迭代中添加的新实体。

以下是更安全的方法:

  1. 首先,遍历你的二维列表并应用act(),但将要删除和添加的实体存储在不同的列表中。不要在act()函数内更改世界。
  2. 在迭代后,将更改应用到世界中。

可能如下所示:

def apply_entity_logic_all():
    to_be_removed = []
    to_be_added = []

    for i in range(len(singleton.World.world)):
        for j in range(len(singleton.World.world[i])):
            for id, entity in enumerate(singleton.World.world[i][j]):
                removal, addition = entity.act(i, j, id)
                to_be_removed.append((i, j, id)) if removal else None
                to_be_added.append((addition, i+1, j)) if addition else None

    for (i, j, id) in to_be_removed:
        del singleton.World.world[i][j][id]

    for (entity, x, y) in to_be_added:
        singleton.World.add(entity, x, y)

def act(self, i, j, id):
    new_object = Plankton(posx=i+1, posy=j)
    return (True, new_object)  # 返回一个元组,指示应删除当前对象并添加新对象

请注意,你可能需要反转to_be_removed列表,以确保你从列表的末尾开始删除实体。这是因为删除较低索引处的元素会导致较高索引处的元素移动,这可能会导致IndexError

for (i, j, id) in reversed(to_be_removed):
    del singleton.World.world[i][j][id]

这些更改确保你在迭代列表时不会修改它,应该可以防止你遇到的问题。

英文:

The issue arises from how you are modifying the list while iterating over it. When you use del singleton.World.world[i][j][id] to delete an element from the list, you're changing the list that you're currently iterating over, which can lead to unexpected behaviors.

The for loop in Python does not dynamically update the list that it is iterating over. So, when you delete an object in the list that you're iterating over, you effectively shift all subsequent items down one index. When you then move to the next index via for id, entity in enumerate(singleton.World.world[i][j]), you're skipping over an entity because everything has moved down one index.

Moreover, you're creating a new entity in the act() method and then immediately add it to the same list you're currently iterating over. That could create a kind of loop where you're iterating over new entities that you're adding while the iteration is still ongoing.

Here's a safer way to do it:

  1. First, iterate through your 2D list and apply act(), but store the entities to be removed and added in separate lists. Do not alter the world within the act() function.
  2. After the iteration, apply the changes to the World.

This could look something like this:

def apply_entity_logic_all():
    to_be_removed = []
    to_be_added = []

    for i in range(len(singleton.World.world)):
        for j in range(len(singleton.World.world[i])):
            for id, entity in enumerate(singleton.World.world[i][j]):
                removal, addition = entity.act(i, j, id)
                to_be_removed.append((i, j, id)) if removal else None
                to_be_added.append((addition, i+1, j)) if addition else None

    for (i, j, id) in to_be_removed:
        del singleton.World.world[i][j][id]

    for (entity, x, y) in to_be_added:
        singleton.World.add(entity, x, y)

def act(self, i, j, id):
    new_object = Plankton(posx=i+1, posy=j)
    return (True, new_object)  # return a tuple indicating that current object should be removed and new one should be added

Please note that you might need to reverse the to_be_removed list to ensure you delete entities starting from the end of the list. This is because deletion of elements at a lower index would cause shifting of elements at higher indices, which may lead to an IndexError.

for (i, j, id) in reversed(to_be_removed):
    del singleton.World.world[i][j][id]

These changes ensure that you're not modifying the list while you're iterating over it, and should prevent the issue you're seeing.

huangapple
  • 本文由 发表于 2023年7月23日 19:52:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76748079.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定