Choosing the Function Dynamically - python

I've got some code that looks like the following:
if command == 'a':
do_a(a, b, c)
elif command == 'b':
do_b(a, b, c)
elif command == 'c':
do_c(a, b, c)
How can I replace this type of thing with something more elegant? Perhaps, something along the lines of do_[command](a, b, c) where the function that is called is dependent upon the command?
Is it even possible?

You can store commands in a dict and lookup when needed:
In [15]: commands = {'mul': lambda x,y: x*y,
'add': lambda x,y: x+y}
In [16]: commands['mul'](3,4)
Out[16]: 12
In [17]: commands['add'](3,4)
Out[17]: 7
In [18]: command = 'add'; vars = (4,5)
In [19]: commands[command](*vars)
Out[19]: 9
you should check whether a command indeed is in commands:
if command in commands:
commands[command]()
else:
# handle error

You could do something like that using "reflection", calling the function by string name, like explained here:
Python: call a function from string name
BUT
That wouldn't be more elegant, it would be less readable, and easier to screw up if you don't have absolute control over what's passed as command.
Your version is just fine:
Explicit is better than implicit.
Simple is better than complex.
If you really want to avoid the elif's I would go with the dict of functions approach that was suggested in the comments.
Keep away from eval for things like this.

def do(fname):
return {'a':do_a, 'b':do_b, 'c':do_c}.get(fname)
def do_a(x,y,z): return x + y + z
def do_b(x,y,z): pass
def do_c(x,y,z): pass
Usage:
do('a')(1,2,3)
6

I'm waiting for a better solution from others but:
def dispatch(cmd, *args):
globals()['do_' + cmd](*args)
def do_a(a, b, c):
Usage:
dispatch('a', 1, 2, 3)
Obviously not robust but dispatch could make sure that the function exists.
Possibly a nice way would be to use decorators.
#command()
def do_a(a,b,c):
...
#command("do_x")
def myfunc(a, b, c):
...
Your decorator could maintain the dispatch lookup table etc.
It depends how worried you are that there might be a need to use existing functions or worry about name collisions.

This might help you, here souce can be any generic module path ending with function name like
module.funcname
So a sample call would look like convertStringToFunction(module.funcname)
def import_module(name):
mod = __import__(name)
components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
def convertStringToFunction(source):
tempArr = source.rsplit('.',1)
mod = import_module(tempArr[0])
func = getattr(mod, tempArr[1])
return func

Related

Require parameter if another argument is present

Following is an example of what I would like to do.
def f(a, b, c):
if a == 'method1':
c = 0
return b + c
In this function, parameter c is unneeded if the condition a='method1' is satisfied.
Still, I can call the function with f(a='method1', b=1, c=2), which will have the same effect as f(a='method1', b=1, c=0).
A solution to this is to set the parameter c default to 0
def f(a, b, c=0):
if a == 'method1':
c = 0
return b + c
Now I can call f(a='method1',b=1), which is exactly what I want. The problem is, I can still change the parameter c in the call f(a='method1',b=1,c=1), which I do not want the user to be able to.
Can I enforce this condition in the function signature, and not in the body (i.e. I would not like to use if in the body). Or if there is another better solution, please tell.
Something like
def f(a, b, c = 0 if a == 'method1', else c is required):
return b + c
Thanks in advance.
a, b and c are all assigned dynamically at runtime. There is no way you can make up for this in the signature. It needs to be detected at runtime and you might as well do that in an if as anywhere else. You can specialize at the function name level, though, and python will take care of detecting the number of parameters.
def f(b,c):
return b + c
def f_method1(b):
return f(b, 0)
def f_method2(half_a_c):
return f(0, half_a_c*2)
Hmm... this almost seems like something that you should be able to do with functools.singledispatch and typing.Literal, but I can't quite get them to work together at least in python 3.7 (with Literal coming from the typing_extensions module). I think that in general singledispatch is probably the only tool that will really get what you've asked for as the different registered functions can have entirely different signatures. However, to do this our methods need to be different classes.
from functools import singledispatch
class Method1():
pass
class OtherMethods():
pass
#singledispatch
def f(method, b, c):
return b + c
#f.register(Method1)
def _(method, b):
return b
f(Method1(), 12) # returns 12
f(Method1(), 12, 7) # raises an error
f(OtherMethods(), 12) # raises an error
f(OtherMethods(), 12, 7) # returns 19
Now this is not exactly what you asked for but the arguments are enforced in the signature.
If someone who knows more about the implementation of singledispatch and Literal comes by maybe they can explain the interaction between the two.
One easier thing to do would be to simply define c to default to some invalid value.
def f(a, b, c=None):
if a == 'method1':
c = 0
return b + c
This solves the problem that if the user forgets to set c for a method besides method1 they'll receive a (possibly cryptic) error message. However it doesn't fix the fact that if they set c when using method1 that value will be silently ignored and possibly cause confusion.

Calling function with unknown number of parameters

I am trying to create a set of functions in python that will all do a similar operation on a set of inputs. All of the functions have one input parameter fixed and half of them also need a second parameter. For the sake of simplicity, below is a toy example with only two functions.
Now, I want, in my script, to run the appropriate function, depending on what the user input as a number. Here, the user is the random function (so the minimum example works). What I want to do is something like this:
def function_1(*args):
return args[0]
def function_2(*args):
return args[0] * args[1]
x = 10
y = 20
i = random.randint(1,2)
f = function_1 if i==1 else function_2
return_value = f(x,y)
And it works, but it seems messy to me. I would rather have function_1 defined as
def function_1(x):
return x
Another way would be to define
def function_1(x,y):
return x
But that leaves me with a dangling y parameter.
but that will not work as easily. Is my way the "proper" way of solving my problem or does there exist a better way?
There are couple of approaches here, all of them adding more boiler-plate code.
There is also this PEP which may be interesting to you.
But 'pythonic' way of doing it is not as elegant as usual function overloading due to the fact that functions are just class attributes.
So you can either go with function like that:
def foo(*args):
and then count how many args you've got which will be very broad but very flexible as well.
another approach is the default arguments:
def foo(first, second=None, third=None)
less flexible but easier to predict, and then lastly you can also use:
def foo(anything)
and detect the type of anything in your function acting accordingly.
Your monkey-patching example can work too, but it becomes more complex if you use it with class methods, and does make introspection tricky.
EDIT: Also, for your case you may want to keep the functions separate and write single 'dispatcher' function that will call appropriate function for you depending on the arguments, which is probably best solution considering above.
EDIT2: base on your comments I believe that following approach may work for you
def weigh_dispatcher(*args, **kwargs):
#decide which function to call base on args
if 'somethingspecial' in kwargs:
return weight2(*args, **kwargs)
def weight_prep(arg):
#common part here
def weight1(arg1, arg2):
weitht_prep(arg1)
#rest of the func
def weight2(arg1, arg2, arg3):
weitht_prep(arg1)
#rest of the func
alternatively you can move the common part into the dispatcher
You may also have a function with optional second argument:
def function_1(x, y = None):
if y != None:
return x + y
else:
return x
Here's the sample run:
>>> function_1(3)
3
>>> function_1(3, 4)
7
Or even optional multiple arguments! Check this out:
def function_2(x, *args):
return x + sum(args)
And the sample run:
>>> function_2(3)
3
>>> function_2(3, 4)
7
>>> function_2(3, 4, 5, 6, 7)
25
You may here refer to args as to list:
def function_3(x, *args):
if len(args) < 1:
return x
else:
return x + sum(args)
And the sample run:
>>> function_3(1,2,3,4,5)
15

Taking Input from a Function Name in Python

I am attempting to write a program to perform arithmetic mod n, given n. I was wondering if there is any way within Python (preferably 2.7) to dynamically define a function such that its behavior depends on the name used to call it. More concretely, I would like to define a function named "*mod", where * is an integer, that then does arithmetic mod *. Perhaps more clearly, I would like to write one function definition for *mod that defines the functions 2mod, 3mod, 4mod, and so on. Is this possible? I apologize if a similar question has already been asked or if my answer is readily available in documentation; I tried to search for it, but I didn't know exactly how to describe the functionality that I'm looking for, so I may have missed it.
Thanks!
You don't want to do that. Just make a simple function and pass both numbers as arguments:
def mod(x, n):
return x % n
print mod(5, 2)
# 1
Well, if you really, really want to, look at this quick hack. It uses a wrapper class to wrap the module in a class, so you can use __getattr__:
import sys
import functools
def add(a, b):
return a + b
def sub(a, b):
return a - b
class Wrapper(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
try:
# quick hack. Don't try this at home :-)
f = ''.join(x for x in name if not x.isdigit())
n = ''.join(x for x in name if x.isdigit())
return functools.partial(getattr(self.wrapped, f), int(n))
except:
return getattr(self.wrapped, name)
sys.modules[__name__] = Wrapper(sys.modules[__name__])
Now, when you call e.g. add10(12) on this module, the result is 22. Note that method names must not start with a number, but you could use names like _add and call the methods like _55add(45) and so on.
But I would follow Haidro advice: You don't want to do that. Just calling the method with two arguments is a lot simpler.
Using globals, lambda:
for i in range(2, 5):
globals()['mod{}'.format(i)] = lambda x, n=i: x % n
assert mod2(4) == 0
assert mod2(3) == 1
assert mod3(2) == 2
assert mod3(1) == 1
assert mod4(1) == 1
assert mod4(2) == 2
assert mod4(3) == 3
assert mod4(9) == 1
You could achieve this by generating the functions as a string, and then exec this string to get the function in the current namespace. Something like:
n = 2
s = 'def mod%i(x):' % n
s += ' return x %% %i' % n
exec s
This would define the function mod2(x)

Python convention for variables that won't be used, but are reuturned from a function nonetheless

If I have a function like the following:
def foo():
return 1, 2
I would normally call the function like:
g, f = foo()
But if I never plan on using the second value returned, is there a way of calling the function that makes that clear, so in the future I won't get distracted by a place-filler variable?
For example, something like:
g, not_used = foo()
Seems like there's probably a standard way to do this that is out there.
You could get the first item directly:
g = foo()[0]
I think pylint recommends _ or dummy:
g, _ = foo()
I usually just ask for the index of the value I care about
g = foo()[0]

Ignore python multiple return value

Say I have a Python function that returns multiple values in a tuple:
def func():
return 1, 2
Is there a nice way to ignore one of the results rather than just assigning to a temporary variable? Say if I was only interested in the first value, is there a better way than this:
x, temp = func()
You can use x = func()[0] to return the first value, x = func()[1] to return the second, and so on.
If you want to get multiple values at a time, use something like x, y = func()[2:4].
One common convention is to use a "_" as a variable name for the elements of the tuple you wish to ignore. For instance:
def f():
return 1, 2, 3
_, _, x = f()
If you're using Python 3, you can you use the star before a variable (on the left side of an assignment) to have it be a list in unpacking.
# Example 1: a is 1 and b is [2, 3]
a, *b = [1, 2, 3]
# Example 2: a is 1, b is [2, 3], and c is 4
a, *b, c = [1, 2, 3, 4]
# Example 3: b is [1, 2] and c is 3
*b, c = [1, 2, 3]
# Example 4: a is 1 and b is []
a, *b = [1]
The common practice is to use the dummy variable _ (single underscore), as many have indicated here before.
However, to avoid collisions with other uses of that variable name (see this response) it might be a better practice to use __ (double underscore) instead as a throwaway variable, as pointed by ncoghlan. E.g.:
x, __ = func()
Remember, when you return more than one item, you're really returning a tuple. So you can do things like this:
def func():
return 1, 2
print func()[0] # prints 1
print func()[1] # prints 2
The best solution probably is to name things instead of returning meaningless tuples (unless there is some logic behind the order of the returned items). You can for example use a dictionary:
def func():
return {'lat': 1, 'lng': 2}
latitude = func()['lat']
You could even use namedtuple if you want to add extra information about what you are returning (it's not just a dictionary, it's a pair of coordinates):
from collections import namedtuple
Coordinates = namedtuple('Coordinates', ['lat', 'lng'])
def func():
return Coordinates(lat=1, lng=2)
latitude = func().lat
If the objects within your dictionary/tuple are strongly tied together then it may be a good idea to even define a class for it. That way you'll also be able to define more complex operations. A natural question that follows is: When should I be using classes in Python?
Most recent versions of python (≥ 3.7) have dataclasses which you can use to define classes with very few lines of code:
from dataclasses import dataclass
#dataclass
class Coordinates:
lat: float = 0
lng: float = 0
def func():
return Coordinates(lat=1, lng=2)
latitude = func().lat
The primary advantage of dataclasses over namedtuple is that its easier to extend, but there are other differences. Note that by default, dataclasses are mutable, but you can use #dataclass(frozen=True) instead of #dataclass to force them being immutable.
Here is a video that might help you pick the right data class for your use case.
Three simple choices.
Obvious
x, _ = func()
x, junk = func()
Hideous
x = func()[0]
And there are ways to do this with a decorator.
def val0( aFunc ):
def pick0( *args, **kw ):
return aFunc(*args,**kw)[0]
return pick0
func0= val0(func)
This seems like the best choice to me:
val1, val2, ignored1, ignored2 = some_function()
It's not cryptic or ugly (like the func()[index] method), and clearly states your purpose.
If this is a function that you use all the time but always discard the second argument, I would argue that it is less messy to create an alias for the function without the second return value using lambda.
def func():
return 1, 2
func_ = lambda: func()[0]
func_() # Prints 1
This is not a direct answer to the question. Rather it answers this question: "How do I choose a specific function output from many possible options?".
If you are able to write the function (ie, it is not in a library you cannot modify), then add an input argument that indicates what you want out of the function. Make it a named argument with a default value so in the "common case" you don't even have to specify it.
def fancy_function( arg1, arg2, return_type=1 ):
ret_val = None
if( 1 == return_type ):
ret_val = arg1 + arg2
elif( 2 == return_type ):
ret_val = [ arg1, arg2, arg1 * arg2 ]
else:
ret_val = ( arg1, arg2, arg1 + arg2, arg1 * arg2 )
return( ret_val )
This method gives the function "advanced warning" regarding the desired output. Consequently it can skip unneeded processing and only do the work necessary to get your desired output. Also because Python does dynamic typing, the return type can change. Notice how the example returns a scalar, a list or a tuple... whatever you like!
When you have many output from a function and you don't want to call it multiple times, I think the clearest way for selecting the results would be :
results = fct()
a,b = [results[i] for i in list_of_index]
As a minimum working example, also demonstrating that the function is called only once :
def fct(a):
b=a*2
c=a+2
d=a+b
e=b*2
f=a*a
print("fct called")
return[a,b,c,d,e,f]
results=fct(3)
> fct called
x,y = [results[i] for i in [1,4]]
And the values are as expected :
results
> [3,6,5,9,12,9]
x
> 6
y
> 12
For convenience, Python list indexes can also be used :
x,y = [results[i] for i in [0,-2]]
Returns : a = 3 and b = 12
It is possible to ignore every variable except the first with less syntax if you like. If we take your example,
# The function you are calling.
def func():
return 1, 2
# You seem to only be interested in the first output.
x, temp = func()
I have found the following to works,
x, *_ = func()
This approach "unpacks" with * all other variables into a "throwaway" variable _. This has the benefit of assigning the one variable you want and ignoring all variables behind it.
However, in many cases you may want an output that is not the first output of the function. In these cases, it is probably best to indicate this by using the func()[i] where i is the index location of the output you desire. In your case,
# i == 0 because of zero-index.
x = func()[0]
As a side note, if you want to get fancy in Python 3, you could do something like this,
# This works the other way around.
*_, y = func()
Your function only outputs two potential variables, so this does not look too powerful until you have a case like this,
def func():
return 1, 2, 3, 4
# I only want the first and last.
x, *_, d = func()

Categories

Resources