2

While working to create a basic text-adventure in the form of "Colossal Cave Adventure", "Zork", etc., I've encountered an issue with my Zombie and Skeleton classes seemingly editing each other.

class Entity(object):
    def __init__(self,name,hp,strength,defense,armor=False,weapon=Fist(),actions=["Attack","Block"]):
        self.name = name
        self.hp = self.maxhp = hp
        self.strength = strength
        self.default_defense = self.defense = defense
        self.armor = armor
        self.weapon = weapon
        self.initiative = 0
        self.actions = actions

    def attack(self,target):
        #An attack action
    def block(self):
        #A block action
    def update(self):
        #Updating the entity

class Zombie(Entity):
    def __init__(self):
        Entity.__init__(self,"Zombie",random.randint(13,20),4,5,Leather())
        print self.actions    #Printing the actions in order to try to fix this issue
        self.actions.remove("Block")
        print self.actions    #Printing the actions in order to try to fix this issue

class Skeleton(Entity):
    def __init__(self):
        Entity.__init__(self,"Skeleton",random.randint(16,23),6,5,False,Bow(999))
        print self.actions    #Printing the actions in order to try to fix this issue
        self.actions.remove("Block")
        print self.actions    #Printing the actions in order to try to fix this issue

monsters = [Zombie(),Skeleton()]

When the code is run, it returns

['Attack','Block']
['Attack']
['Attack']
#Error message

The error says that 'Block' isn't in the Skeleton's self.actions to remove, but as far as my understanding goes, 'Block' should be in there when Entity.__init__ is called. If I switch Zombie() and Skeleton() in monsters, the problem still happens, so the problem seems to be that the first subclass is removing the entry from both subclasses.

I'm new to subclasses, so it's very likely that the issue is in my limited understanding of how they work. Is this the intended behavior? If so, how would I get the behavior I'm looking for?

2 Answers2

2

The default argument for __init__ is only evaluted once. So if you don't give something else for actions every instance of Entity will reference the exact same list. And when you remove from that list in one instance the list in the other instances is also modified.

To prevent this from happening try this:

class Entity:
    def __init__(self, ... actions=None, ...):
    ...
    if actions is None:
        self.actions = ["Attack", "Block"]
    else:
        self.actions = actions

Then the actions list is created for every instance.

Wombatz
  • 4,958
  • 1
  • 26
  • 35
0

The problem is that you are using a list as the default argument to actions in the __init__ of Entity.

Each of your subclasses holds a reference to the original list ["Attack", "Block"]. Whenever you modify it, the original list is updated, which is probably not what you were expecting.

To avoid that sort of error use immutable types as default arguments, e.g. tuple instead of list.

karlson
  • 5,325
  • 3
  • 30
  • 62