Sal
Sal

Reputation: 1

In Python, how do you execute objects that are functions from a list?

I'm fairly new at Python and can't seem to get this to work. I have a list which has an imbedded list of objects which are the names of functions and I need to execute the list of objects. I have functions that call lists of functions. I want to change this so I can call the list of functions directly without calling another function.

validActions=[
  ['Refresh','Environment Refresh',Refresh],
  ['Retire','Environment Retire Storage',
    [ doStatusDecommission,
      doPauseJobBeforeStart,
      doRetireStorage,
      doStatusDisconnected]],
  ['Provision','Environment Provision Storage',Provision]
]

def updateEnv(ctx):
  for actionVal,actionDesc,actionFunction in validActions:
    if ctx["newAction"] == actionVal:
      actionFunction()

This works if I'm calling "Refresh" or "Provision" as they are functions. However, this does not work when I call the list for "Retire" and the error message is

TypeError: 'list' object is not callable

Upvotes: 0

Views: 164

Answers (5)

Patrick the Cat
Patrick the Cat

Reputation: 2158

I don't want to fill my answer with your defined list to make it look longer. I'll use the same list you defined.

def updateEnv(ctx):
    for actionVal,actionDesc,actionFunctions in validActions:
        if ctx["newAction"] == actionVal:
            try:
                [func() for func in actionFunctions]
            except TypeError:
                # not iterable
                actionFunctions()
            except Exception as e:
                # iterable, unexpected errors
                print e
                pass

If you can modify the data structure, I suggest the following:

validActions={
  ('Refresh','Environment Refresh') : {Refresh},
  ('Retire','Environment Retire Storage'):
    { doStatusDecommission,
      doPauseJobBeforeStart,
      doRetireStorage,
      doStatusDisconnected } ,
  ('Provision','Environment Provision Storage'):{Provision}
}

Your functions are generally method descriptor, which are immutable. Using set as container of your function can greatly reduce time cost in iterating and counting.

By using dictionary as top-level container with tuple keys, you can accurately map your input to output. What does that mean? It means:

def updateEnv(ctx):
    [[func() for func in value] if ctx["newAction"] == key[0] 
     else None
     for key, value in validActions.items()]

Even more efficiently, if you know actionDecs is unique to actionVal, use two separate mappings:

validActions = {
   'Refresh' : {Refresh},
   'Retire':
      { doStatusDecommission,
       doPauseJobBeforeStart,
       doRetireStorage,
       doStatusDisconnected } ,
   'Provision' : {Provision}
}

actionsDescs = {
    'Refresh': 'Environment Refresh'
    'Retire': 'Environment Retire Storage'
    'Provision' : 'Environment Provision Storage'
}

If you need to description from abbreviation, call for example:

actionsDescs['Refresh']

And you function iteration becomes:

def updateEnv(ctx):
    [func() for func in validAction[ctx["newAction"]]]

Feel free to comment if you have specific need or question on this.

Upvotes: 0

Ryan Haining
Ryan Haining

Reputation: 36792

One option is to have each list end with a list of functions, even if the list only has one element

validActions=[
  ['Refresh','Environment Refresh', [Refresh]], #note brackets around Refresh
  ['Retire','Environment Retire Storage',
    [ doStatusDecommission,
      doPauseJobBeforeStart,
      doRetireStorage,
      doStatusDisconnected]],
  ['Provision','Environment Provision Storage',[Provision]]
]

def updateEnv(ctx):
  for actionVal,actionDesc,actionFunctions in validActions:
    if ctx["newAction"] == actionVal:
      for func in actionFunctions:
        func()

Or make a new function which calls all 4 of those functions

def retireFunctions():
  doStatusDecommission()
  doPauseJobBeforeStart()
  doRetireStorage()
  doStatusDisconnected()

validActions=[
  ['Refresh','Environment Refresh',Refresh],
  ['Retire','Environment Retire Storage', retireFunctions],
  ['Provision','Environment Provision Storage',Provision]
]

Or a final option is a type test (not recommended)

def updateEnv(ctx):
  for actionVal,actionDesc,actionFunction in validActions:
    if ctx["newAction"] == actionVal:
      if callable(actionFunction):
        actionFunction()
      else:
        for func in actionFunction:
          func()

Upvotes: 1

Martijn Pieters
Martijn Pieters

Reputation: 1121486

You can still call each function in the list. If you make all entries lists then it becomes much easier to handle both cases:

validActions=[
    ['Refresh','Environment Refresh', [Refresh]],
    ['Retire','Environment Retire Storage', [
        doStatusDecommission,
        doPauseJobBeforeStart,
        doRetireStorage,
        doStatusDisconnected
    ]],
    ['Provision', 'Environment Provision Storage', [Provision]]
]

def updateEnv(ctx):
    for actionVal, actionDesc, actionFunctions in validActions:
        if ctx["newAction"] == actionVal:
            for action_function in actionFunctions:
                action_function()

If all you are doing is finding the one action that matches ctx['newAction'], you'd be better off using a dictionary and look up the actionDesc and actionFunctions items from that *directly:

validActions = {
    'Refresh': ('Environment Refresh', (Refresh,)),
    'Retire': ('Environment Retire Storage', (
        doStatusDecommission,
        doPauseJobBeforeStart,
        doRetireStorage,
        doStatusDisconnected
    ),
    'Provision': ('Environment Provision Storage', (Provision,)),
}

def updateEnv(ctx):
    actionDesc, actionFunctions = validActions[ctx["newAction"]]
    for action_function in actionFunctions:
         action_function()

Upvotes: 3

eri
eri

Reputation: 3504

To get function by name

If functions defined in this module:

globals().get(actionFunctionName)(args)

If in other module or class:

getattr(class_or_module,actionFunctionName)(args)

Upvotes: -1

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 798536

Make all of them lists and then iterate over the list executing each in turn.

for actionVal,actionDesc,actionFunctions in validActions:
  if ctx["newAction"] == actionVal:
    for actionFunction in actionFunctions:
      actionFunction()

Upvotes: 1

Related Questions