1

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.

7
  • @Downvoter/close voter: What additional information do you need, exactly? Commented Apr 30, 2014 at 18:50
  • 1
    I didn't downvote, but you need to add your actual code so we can see what you're talking about. Commented Apr 30, 2014 at 18:51
  • The brute force/naive approach would probably be to call the passed function in a series of try/except blocks with the different permutations until one succeeded. With only 4 possibilities, that might even be a better solution than using inspect. Another important point though here is whether this function is itself an argument, or being registered to some sort of handler. If it is a registry of some sort, it may well be worth doing some preprocessing to categorize the function the first time you see it, then call it intelligently thereafter. Commented Apr 30, 2014 at 18:52
  • Wait, so foo and bar might not only be arguments to the function, but also instances from which you are referencing the function? That's a more complicated scenario... Commented Apr 30, 2014 at 18:59
  • 1
    Your example is almost there, just give the lambdas a uniform signature. Commented Apr 30, 2014 at 19:00

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

2 Comments

My problem is that I have many functions I want to call this way, and I don't really want to change all of the functions to fit the call.
Well, this is the most comprehensive answer, and it tells me that there really isn't a way to do what I want. Thanks.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.