Reputation: 3481
It appears that one cannot pass dicts with non-string keywords to functions in Python. (I see this has been brought up before here)
However, before coming up with some sort of convoluted solution to my problem, I figured I would ask the experts and see if there is something I'm missing. Unfortunately, I cannot change this paradigm easily without significant re-factoring, as a previous author never anticipated this type of action (this is a very simple example).
I have objects called services
that can perform actions on nodes
. Some nodes
require different service
configurations. Instead of a service
instance per node
, I have one instance of each service
, which I then combine with configuration parameters per node
. The service
and it's corresponding configuration parameters are stored in a dict
.
Both the node
along with its services
and their corresponding configuration parameters are passed to a function (setupServices
) which then processes everything. This function must be able to call functions which are part of the service
object. Unfortunately, this fails with the error:
TypeError: setupServices() keywords must be strings
If you're wondering what's passing it as **nodeServices
instead of just nodeServices
, it is the code segment below (value would be nodeServices
in this case). I'm hesitant to make this code specific for an individual case, as it acts as a backbone for many other things.
if type( value ) is list:
result = f( *value )
elif type( value ) is dict:
result = f( **value )
else:
result = f( value )
results[ name ] = result
return result
Are there any other ways I can accomplish this with minimal modification?
"A node can be associated with many services"
class Node(object):
pass
"""Setup services routine (outside of the node due to an existing design choice)
I could rewrite, but this would go against the existing author's paradigms"""
def setupServices ( node, **nodeServices ):
for object, objOverrides in nodeServices.items():
"do something with the service object"
"2 simple services that can be perform actions on nodes"
class serviceA(object):
pass
class serviceB(object):
pass
"instantiate an object of each service"
a = serviceA()
b = serviceB()
"Create a node, assign it two services with override flags"
node = Node()
nodeServices = {}
nodeServices[a] = { 'opt1' : True }
nodeServices[b] = { 'opt2' : False }
"Pass the node, and the node's services for setup"
setupServices ( node, **nodeServices )
Upvotes: 1
Views: 208
Reputation: 251428
The simplest way to change that is to change setupServices
so it no longer accepts keyword arguments, but just a dict of options. That is, change its definition to:
def setupServices (node, nodeServices):
(removing the **
). You can then call it with setupServices(node, nodeServices)
.
The only downside to this is that you must pass in a dict, and cannot pass literal keyword arguments. With the keyword-argument setup, you could call setupServices(node, opt1=True)
, but without keyword arguments, if you wanted to specify that option "inline" you would have to do setupServices(node, {'opt1': True})
, passing in a literal dict. However, if you're always programmatically creating a dict of options anyway and then passing it with **
, this downside won't really matter. It only matters if you actually have code that is passing literal keyword arguments like setupServices(node, foo=bar)
.
As the answer in the question you linked to says, there is no way to make functions accept non-string keyword arguments. So there is no way you can get foo(**nodeServices)
to work when the keys of nodeServices
aren't strings. (Given that, it's a little hard to understand how this scheme could have ever worked, since in your example it seems like setupServices
was always expecting a dict whose keys were service objects, which means passing them as keyword arguments never could have worked.)
Upvotes: 2