Reputation: 1
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
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
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
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
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
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