Nathan Merrill
Nathan Merrill

Reputation: 8386

Python: Passing conditional parameters by name

I have a bunch of functions, some of them require a bar parameter, some of them require a foo parameter, some of them require neither, and some require both.

I am hoping to call all of these functions from the same location, as the function is referenced from a variable.

The possible solutions I have found is:

  1. function(bar = "some_value", foo = "other value")
  2. Using inspect and then detecting which arguments need to be passed in

I don't believe #1 will work, but I know that #2 will. Is there a native (not using a library) way for me to do this?

As an example:

functions = [lambda foo: foo.do_something(),
             lambda bar: bar.do_something_else(),
             lambda foo, bar: foo.something_with_bar(bar),
             lambda: do_another_task()
]
for function in functions:
    //call goes here

Note: I actually have many, many functions that I want to call with these parameters, and they aren't all stored nicely in an array. I would prefer if I could do it without changing the functions themselves.

Upvotes: 1

Views: 170

Answers (2)

shx2
shx2

Reputation: 64318

You might be better off storing a lambda expression in a variable, instead of the underlying function, and binding your arguments in the lambda. Something like:

foo = 'foo'
bar = 'bar'
if funcname == 'a':
  func = a  # a takes no args
elif funcname == 'b':
  func = lambda: bb(bar=bar)  # bb takes bar
elif funcname == 'c':
  func = lambda: ccc(foo=foo)  # ccc takes foo
# call it, uniformly
func()

A similar approach would be to "de-bind":

if funcname == 'a':
  func = lambda foo, bar: a()  # don't pass foo and bar
elif funcname == 'b':
  func = lambda foo, bar: bb(bar=bar)  # don't pass foo
elif funcname == 'c':
  func = lambda foo, bar: ccc(foo=foo)  # don't pass bar
# call it, uniformly
func(foo, bar)

Furthermore, if you want to refactor your code to make it more elegant and object-oriented, define an interface and facade implementations:

class FooBarInterface(object):
    def f(self, foo, bar):
        raise NotImplementedError
class A(FooBarInterface):
    def f(self, foo, bar):
        return a()
class B(FooBarInterface):
    def f(self, foo, bar):
        return bb(bar=bar)
class C(FooBarInterface):
    def f(self, foo, bar):
        return ccc(foo=foo)

if funcname == 'a':
  obj = A()
elif funcname == 'b':
  obj = B()
elif funcname == 'c':
  obj = C()
# call it, uniformly
obj.f()

And please don't use introspection. It would make your code way more complicated than need be.

Upvotes: 3

Samuel Colvin
Samuel Colvin

Reputation: 13279

No way to do it natively unless you can edit the functions themselves, if you have you could have

def func_a(**kwargs):
    foo = kwargs['foo']
    # or even:
    foo = kwargs.get('foo', None) # in case foo wasn't passed the the function
    ...

def func_b(**kwargs):
    bar = kwargs.get('bar', None)
    ...

With the functions setup that way you can pass any key word arguments you like.

Upvotes: 0

Related Questions