Python Function as a Function Argument

How to pass function as an argument to another function, without running it automatically

The problem is that you are calling your function when you're trying to pass it as a parameter. Instead of passing it as someFunction(...), pass it as someFunction.

e.g.

a = testObject("testName2", someFunction )

instead of what you have. This will pass the function 'object', ready to be called. Then, inside the other function, simply do:

def testObject(name, funct):
if name == "testName":
b = funct([0,0,0], 1, 6)

I assume you'd want to pass the values as different arguments, as I assume these aren't constants, but this should be enough to guide you.

If you want to pass it in with arguments already set, I'd definitely give a look into functools.partial, but keep in mind that it might become a bit messy.

For partial:

from functools import partial

partial_function = partial(someFunction, [0,0,0], 1, 6)
a = testObject("testName2", partial_function )

------
def testObject(name, funct):
if name == "testName":
b = funct()

How do I pass a method as a parameter in Python

Yes it is, just use the name of the method, as you have written. Methods and functions are objects in Python, just like anything else, and you can pass them around the way you do variables. In fact, you can think about a method (or function) as a variable whose value is the actual callable code object.

Since you asked about methods, I'm using methods in the following examples, but note that everything below applies identically to functions (except without the self parameter).

To call a passed method or function, you just use the name it's bound to in the same way you would use the method's (or function's) regular name:

def method1(self):
return 'hello world'

def method2(self, methodToRun):
result = methodToRun()
return result

obj.method2(obj.method1)

Note: I believe a __call__() method does exist, i.e. you could technically do methodToRun.__call__(), but you probably should never do so explicitly. __call__() is meant to be implemented, not to be invoked from your own code.

If you wanted method1 to be called with arguments, then things get a little bit more complicated. method2 has to be written with a bit of information about how to pass arguments to method1, and it needs to get values for those arguments from somewhere. For instance, if method1 is supposed to take one argument:

def method1(self, spam):
return 'hello ' + str(spam)

then you could write method2 to call it with one argument that gets passed in:

def method2(self, methodToRun, spam_value):
return methodToRun(spam_value)

or with an argument that it computes itself:

def method2(self, methodToRun):
spam_value = compute_some_value()
return methodToRun(spam_value)

You can expand this to other combinations of values passed in and values computed, like

def method1(self, spam, ham):
return 'hello ' + str(spam) + ' and ' + str(ham)

def method2(self, methodToRun, ham_value):
spam_value = compute_some_value()
return methodToRun(spam_value, ham_value)

or even with keyword arguments

def method2(self, methodToRun, ham_value):
spam_value = compute_some_value()
return methodToRun(spam_value, ham=ham_value)

If you don't know, when writing method2, what arguments methodToRun is going to take, you can also use argument unpacking to call it in a generic way:

def method1(self, spam, ham):
return 'hello ' + str(spam) + ' and ' + str(ham)

def method2(self, methodToRun, positional_arguments, keyword_arguments):
return methodToRun(*positional_arguments, **keyword_arguments)

obj.method2(obj.method1, ['spam'], {'ham': 'ham'})

In this case positional_arguments needs to be a list or tuple or similar, and keyword_arguments is a dict or similar. In method2 you can modify positional_arguments and keyword_arguments (e.g. to add or remove certain arguments or change the values) before you call method1.

Python: default value of function as a function argument

You can do the following:

def myF(a, b=None):
if b is None:
b = a - 1
return a * b - 2 * b

How to pass a function as a function parameter in Python

Functions are first-class citizens in Python. you can pass a function as a parameter:

def iterate(seed, num, fct):
# ^^^
x = seed
orbit = [x]
for i in range(num):
x = fct(x)
# ^^^
orbit.append(x)
return orbit

In your code, you will pass the function you need as the third argument:

def f(x):
return 2*x*(1-x)

iterate(seed, num, f)
# ^

Or

def g(x):
return 3*x*(2-x)

iterate(seed, num, g)
# ^

Or ...


If you don't want to name a new function each time, you will have the option to pass an anonymous function (i.e.: lambda) instead:

iterate(seed, num, lambda x: 3*x*(4-x))

Passing a function as an argument using internal parameters

I'm not sure I have understood what you want.
For example this:

def fn2(a: str):
print("fn2 string print", a)

def fn1(func):
b = "fn1 string going to pass to fn2"
func(b)

fn1(fn2)

Outputs this: fn2 string print fn1 string going to pass to fn2

If this is what you want, you can simply call the function fn2 inside fn1

How can I write my own function that accepts and uses a function as a parameter?

A function accepting other functions as argument or returning functions is called higher-order function.

So Panda's apply() is a higher-order function. It accepts another function like e.g. sum and calls or invokes it internally as sum(args).

To define a higher-order function that accepts a function as argument use a signature as with common functions.

In the examples below this will be def md_heading(phrase):.

How to call the functional parameter (without arguments)

See how to invoke the argument which is expected to be a function:

def md_heading(phrase):
return "# " + phrase()

Note:

  • The parameter was named like a variable name here - not with the explicit notation of requiring a function like in Panda's apply(func).
  • Still this parameter phrase is expected to be a function-reference because it will be invoked using parentheses (without argument).
  • md_heading() is the wrapper function, phrase the functional parameter.

Passing a literal

Now try what happens when passing a string literal like `'Hello World' as argument.

result = md_heading('Hello World')  # this would pass it in, then try to call it .. and ?
print(result)

The invocation of the string passed as argument will raise an error.
In exactly that statement return "# " + phrase() as:

TypeError: 'str' object is not callable

Passing a function (reference) as argument

Now you should pass a function as argument, but just as reference (without parentheses):

def greeting():
return 'Hi'

result = md_heading(greeting)
print(result)

Prints: Hi

Passing a lambda as argument

Or you can pass a lambda as argument:

result = md_heading(lambda: "Hello!")
print(result)

Prints: 'Hello!

Note: The lambda must not have a positional argument. This the closes to passing a constant, like the string from the beginning.

No (required) parameters when passing lambdas

When we define the lambda with a positional argument like lambda name: "Hello " + name here, it will raise: TypeError: <lambda>() missing 1 required positional argument: 'name' because in our higher-order function it is invoked without argument, just as phrase().

Function having sub functions and their arguments as arguments

Note, the functions should be functions, not strings, for this implementation to work

If you want to return the function called with a set of kwargs, you're pretty close. I would use a positional argument for func, then you can pass kwargs into func, which is a bit more explicit:

def myfunc(func, **kwargs):
return func(**kwargs)

Then, you could wrap each pair of func, **kwargs as tuples, and do a for loop:

# This would be called like
somelist = [(np.random.normal, { 'loc' : 0 , 'scale' : 1 , 'size' : 7 }),
(np.random.uniform , { 'low' : 0 , 'high' : 1 , 'size' : 7 })]

results = []

# append results to a list
for func, kwargs in somelist:
results.append(myfunc(func, **kwargs))

By doing it this way, you don't have to worry about what you name any of your variables, and it's a bit more readable. You know that the loop will be dealing with pairs of items, in this case func, kwarg pairs, and your function can handle those explicitly

Handling the string calls

So there are a few ways to accomplish this task that are a bit more tricky, but overall shouldn't be horrible. You'll need to modify myfunc to handle the function name:

# func is now a string, unlike above

def myfunc(func, **kwargs):
# function will look like module.class.function
# so split on '.' to get each component. The first will
# be the parent module in global scope, and everything else
# is collected into a list
mod, *f = func.split('.') # f is a list of sub-modules like ['random', 'uniform']
# func for now will just be the module np
func = globals().get(mod)
for cls in f:
# get each subsequent level down, which will overwrite func to
# first be np.random, then np.random.uniform
func = getattr(func, cls)
return func(**kwargs)

The reason I'm using globals().get(mod) is a) I'm assuming you might not always be using the same module, and b) calling a renamed import from sys.modules will yield a KeyError, which isn't what you want:

import sys
import numpy as np

sys.modules['np'] # KeyError

sys.modules['numpy']
# <module 'numpy.random' from '/Users/mm92400/anaconda3/envs/new36/lib/python3.6/site-packages/numpy/random/__init__.py'>

# globals avoids the naming conflict
globals()['np']
# <module 'numpy.random' from '/Users/mm92400/anaconda3/envs/new36/lib/python3.6/site-packages/numpy/random/__init__.py'>

Then getattr(obj, attr) will return each subsequent module:

import numpy as np

getattr(np, 'random')
# <module 'numpy.random' from '/Users/mm92400/anaconda3/envs/new36/lib/python3.6/site-packages/numpy/random/__init__.py'>

# the dotted access won't work directly
getattr(np, 'random.uniform')
# AttributeError

So, in total:

import numpy as np

func, kwargs = ('np.random.normal', { 'loc' : 0 , 'scale' : 1 , 'size' : 7 })

myfunc(func, **kwargs)

array([ 0.83276777, 2.4836389 , -1.07492873, -1.20056678, -0.36409906,
-0.76543554, 0.90191746])

And you can just extend that to the code in the first section



Related Topics



Leave a reply



Submit