-2

I've run into a situation wherein an unused keyword argument in a class initialization leads to unwanted sharing of properties.

The ActionsAgent class below has a actions property that is intended to hold a list of pending actions, and a addActions method that appends actions to that list. Its initialization allows an initial action list to be supplied with a keyword argument that defaults to [].

###############
# Define

class ActionsAgent(object):
  def  __init__( self, name="",actions=[]):
    self.name = name
#     self.actions = []
    self.actions = actions
    print("init {}: {}".format(self.name,self.actions))
  def addActions(self,*actions):
    print("addActions {}, enter: {}".format(self.name,self.actions))
    for action in actions:
      self.actions.append(action)
    print("addActions {}, exit: {}".format(self.name,self.actions))

The following test defines two agents, alice and bob, and adds two actions to each.

##############
# TEST
alice = ActionsAgent(name="alice")
bob = ActionsAgent(name="bob")
alice.addActions("hop", "skip")
bob.addActions( "run", "jump" )

print("alice end actions", alice.actions)
print("bob end actions", bob.actions)

The expected behavior is for alice's actions to be "hop" and "skip, and for bob's to be "run" and "jump". However upon running the test, it is shown that bob shares alice's actions when running addActions and that upon exit both agents share the same actions.

init alice: []
init bob: []
addActions alice, enter: []
addActions alice, exit: ['hop', 'skip']
addActions bob, enter: ['hop', 'skip']
addActions bob, exit: ['hop', 'skip', 'run', 'jump']
alice end actions ['hop', 'skip', 'run', 'jump']
bob end actions ['hop', 'skip', 'run', 'jump']

Note, that if the two agents are supplied the actions keyword (alice = ActionsAgent(name=alice,actions=[]), then the test run's as expected. If the initialization sets actions to [] the test also runs as expected.

What is going on here?

trevor cook
  • 1,531
  • 8
  • 22

1 Answers1

-2

It seems that when ActionsAgent is initialized without the actions keyword, a new variable is created and assigned to self.actions. Since this variable is not a simple data (such as an integer or None), its reference (not value) is assigned to self.actions. Hence, any instances not assigned that keyword will share that same reference.

I noted in the question two ways in which I can get my expected result. A better way is to use a None in the initialization function:

class ActionsAgent(object):
  def  __init__( self, name="",actions=None):
    self.name = name
    if actions is None:
      self.actions = []
    else:
      self.actions = actions
    print("init {}: {}".format(self.name,self.actions))
trevor cook
  • 1,531
  • 8
  • 22