Python methods and "switches" - python

I'm trying to use a dictionary as a switch statement as in
def add(first, second):
return first + second
def sub():
...
return something
operations = {
"Add": add,
"Sub": sub
}
ret_val = operations[operation]
Now how can I pass the arguments to add and sub and get their response? Currently, I don't pass anything to the methods, and testing the ret_val. What I see is the operation getting called, but the return doesn't come back. What I get is the pointer to the operation method.
Thanks!

To call a function, put the arguments in parentheses after it, just like when you call a function directly by its name.
ret_val = operations[operation](1, 2)
Note that for this to work properly, all the functions in operations need to take the same number of arguments. So it won't work if add() takes two arguments but sub() takes none, as you've shown.
If the functions can take different numbers of arguments, you could put the arguments in a list and use the unpacking operator.
args = (1, 2)
ret_val = operations[operation](*args)
Then you just have to ensure that args contains the appropriate number of arguments for the particular operation.

The dictionary contains callable functions. To call them, just add the arguments in parentheses.
operations[operation](arg1, ...)

So, the main thing you're missing is executing the function call. The code as provided grabs the function reference properly, but you need parens to execute it.
Once you execute it, you need some way to pass arguments. Because the number of args varies by function, the best way is to pass both a variable number of args list (*args) and a dictionary of keyword arguments (**kwargs).
I've filled in your pseudocode slightly so these run:
def add(first, second):
return first + second
def sub(first, second):
return first - second
operations = {
"Add": add,
"Sub": sub,
}
Call add with args:
op = 'Add'
op_args = [1, 2]
op_kwargs = {}
ret_val = operations[operation](*op_args, **op_kwargs)
print(ret_val)
3
Call add with kwargs:
op = 'Add'
op_args = []
op_kwargs = {'first': 3, 'second': 4}
ret_val = operations[operation](*op_args, **op_kwargs)
print(ret_val)
7
If you try to pass both args and kwargs in a conflicting way, it will fail:
# WON'T WORK
op = 'Add'
op_args = [1, 2]
op_kwargs = {'first': 3, 'second': 4}
ret_val = operations[operation](*op_args, **op_kwargs)
print(ret_val)
TypeError: add() got multiple values for argument 'first'
But you can use both in a complementary way:
op = 'Add'
op_args = [1]
op_kwargs = {'second': 4}
ret_val = operations[operation](*op_args, **op_kwargs)
print(ret_val)
5
One technical note is that the naming args and kwargs is purely convention in Python. You could call them whatever you want. An answer that discusses the two more is available here: https://stackoverflow.com/a/36908/149428.
Note that I did not do any input validation, etc for the purpose of a simple, focused answer. If you're getting input from a user, that's an important step to remember.

Related

Call specific function within function depending on user input [duplicate]

For instance, I've tried things like mydict = {'funcList1': [foo(),bar(),goo()], 'funcList2': [foo(),goo(),bar()], which doesn't work.
Is there some kind of structure with this kind of functionality?
I realize that I could obviously do this just as easily with a bunch of def statements:
def func1():
foo()
bar()
goo()
But the number of statements I need is getting pretty unwieldy and tough to remember. It would be nice to wrap them nicely in a dictionary that I could examine the keys of now and again.
Functions are first class objects in Python and so you can dispatch using a dictionary. For example, if foo and bar are functions, and dispatcher is a dictionary like so.
dispatcher = {'foo': foo, 'bar': bar}
Note that the values are foo and bar which are the function objects, and NOT foo() and bar().
To call foo, you can just do dispatcher['foo']()
EDIT: If you want to run multiple functions stored in a list, you can possibly do something like this.
dispatcher = {'foobar': [foo, bar], 'bazcat': [baz, cat]}
def fire_all(func_list):
for f in func_list:
f()
fire_all(dispatcher['foobar'])
# Lets say you have 10 programs or functions:
func_list = [program_001, program_002, program_003, program_004, program_005,
program_006, program_007, program_008, program_009, program_010]
choose_program = int(input('Please Choose a program: ')) # input function number
func_list[choose_program - 1]()
Case 1: Without Params.
The way that is employed to achieve this task is that the function name is kept as dictionary values, and while calling with keys, brackets ‘()’ are added.
# Python3 code to demonstrate the working of
# Functions as dictionary values
# Using Without params
# call Gfg fnc
def print_key1():
return "This is Gfg's value"
# initializing dictionary
# check for function name as key
test_dict = {"Gfg": print_key1, "is": 5, "best": 9}
# printing original dictionary
print("The original dictionary is : " + str(test_dict))
# calling function using brackets
res = test_dict['Gfg']()
# printing result
print("The required call result : " + str(res))
Case 2: With params
The task of calling with params is similar to the above case, the values are passed during the function call inside brackets as in usual function calls.
# Python3 code to demonstrate the working of
# Functions as dictionary values
# Using With params
# call Gfg fnc
def sum_key(a, b):
return a + b
# initializing dictionary
# check for function name as key
test_dict = {"Gfg": sum_key, "is": 5, "best": 9}
# printing original dictionary
print("The original dictionary is : " + str(test_dict))
# calling function using brackets
# params inside brackets
res = test_dict['Gfg'](10, 34)
# printing result
print("The required call result : " + str(res))
The original dictionary is: {'Gfg': <function sum_key at 0x7f538d017e18>, 'is': 5, 'best': 9}
The required call result: 44
SOURCE: geeksforgeeks

Is it possible to pass the same optional arguments to multiple functions?

I want to ask if there is a way to prevent unnecessary duplicate of code when passing the same arguments into a function's optional arguments.
Hopefully the following example provides a good idea of what I am trying to do:
def f(arg1):
def g(optional_1=0, optional_2=0, optional_3=0):
return arg1+optional_1+optional_2+optional_3
return g
b, c = 2, 3
f1 = f(1)
f2 = f(2)
calc_f1 = f1(optional_2=b, optional_3=c)
calc_f2 = f2(optional_2=b, optional_3=c)
As you can see, f1 and f2 only differ in the arg1 passed into f and afterwards I call them with the same variables for the same optional arguments.
It is fine when the code is short, but when I have over 10 optional arguments, it becomes unnecessarily long and redundant.
Is it possible to do something like
optional_variable_pair = #some way to combine them
calc_f1 = f1(optional_variable_pair)
calc_f2 = f2(optional_variable_pair)
so I get a more succinct and easy to read code?
Any function with multiple optional arguments is a bit smelly because:
you get so many argument combinations that it requires a large amount of testing.
because of all the options the function has to have alot of conditionals and routes which increase its cyclomatic complexity.
You can apply a refactoring to extract the whole argument list into an Object and have the function work on that object. This works really well if you can find a unifying name that describes your argument list and fits whatever metaphor you are using around the function. You can even invert the call so that the function becomes a method of the Object, so you get some encapsulation.
To answer the question you asked, the answer is yes. You can do almost exactly what you want using keyword argument unpacking.
def f(arg1):
def g(optional_1=0, optional_2=0, optional_3=0):
return arg1+optional_1+optional_2+optional_3
return g
optional_variable_pair = {
'optional_2': 2,
'optional_3': 3
}
f1 = f(1)
f2 = f(2)
calc_f1 = f1(**optional_variable_pair)
calc_f2 = f2(**optional_variable_pair)
If I'm reading your intent correctly, though, the essence of your question is wanting to pass new first arguments with the same successive arguments to a function. Depending on your use case, the wrapper function g may be unnecessary.
def f(arg1, *, optional_1=0, optional_2=0, optional_3=0):
return optional_1 + optional_2+optional_3
optional_variable_pair = {
'optional_2': 2,
'optional_3': 3
}
calc_f1 = f(1, **optional_variable_pair)
calc_f2 = f(2, **optional_variable_pair)
Obviously, if the first argument continues incrementing by one, a for loop is in order. Obviously, if you are never using the optional_1 parameter, you do not need to include it. But, moreover, if you find yourself using numbered arguments, there is a good chance you really should be working with tuple unpacking instead of keyword unpacking:
def f(*args):
return sum(args)
optional_variable_pair = (2, 3)
for i in range(1, 3):
calc = f(i, *optional_variable_pair)
# ...do something with calc...
You may also be interested in researching functools.partial, as well, which can take the place of your wrapper function g, and allow this:
import functools
def f(*args):
return sum(args)
f1 = functools.partial(f, 1)
f2 = functools.partial(f, 2)
calc_f1 = f1(2, 3) # = 1 + 2 + 3 = 6
calc_f2 = f2(2, 3) # = 2 + 2 + 3 = 7
You use key-value pairs as function argsuments, for this purpose you can use *args and **kwargs:
optional_variable_pair = {
"optional_1": 1,
"optional_2": 2,
"optional_3": 3,
}
calc_f1 = f1(**optional_variable_pair)
calc_f2 = f2(**optional_variable_pair)

Wrap function without clobbering default arguments

Is there a way to forward function arguments without hiding the fact that the original call did or did not provide optional arguments?
def func1(a=x):
# do stuff
def func2(b=y):
# pass args to func1 without masking func1 defaults
return func1(?)
A call to func2() should result in func1() being called without arguments or at least with its default arguments, whatever they may be.
The following almost works but fundamentally I don't know if there is a way for func2 to determine if its defaults were invoked on not.
def func2(b=y):
# this comes close but what if func2(y) is called?
if b == y:
return func1()
else:
return func1(b)
The usual way of determining if a parameter is left off is to use None as the default. It's unlikely that you'll be calling a function with None so it's a useful marker.
def func2(b=None):
if b is None:
return func1()
else:
return func1(b)
I suspect the right way to do this is to have your func2 function use a sentinel value as its default argument, so you can recognize it easily. If you get that sentinel, you can set up the arguments you'll pass on to func1 however you want (e.g. not passing any argument). You can use argument unpacking to handle passing a variable number of arguments (such as 0-1).
A common sentinel is None, though if that could be a meaningful value for a caller to pass, you may want to use something else (an instance of object is a common choice). Here's an example:
def func1(a="default value"): # lets assume we don't know what this default is
# do stuff with a
# later, perhaps in a different module
_sentinel = object() # our sentinel object
def func2(b=_sentinel):
if b is _sentinel: # test for the sentinel
b = "some useful value"
a_args = () # arguments to func1 is an empty tuple
else:
a_args = (b,) # pack b into a 1-tuple
# do stuff with b perhaps
func1(*a_args) # call func1 with appropriate arguments (either b or nothing)
Note that this design is relatively silly. Most of the time you'll either call func1 with an argument in all cases, or you'll call it without an argument in all cases. You rarely need to conditionally pass an argument like this.
See this answer:
https://stackoverflow.com/a/2088101/933416
There is no way to get the information you want from the internals. To detect whether defaults were used, you would need to re-implement the internal default argument processing within the function, i.e.:
def func2(*args, **kwargs):
if len(args) == 0 and "b" not in kwargs:
b = y
return func1()
else:
return func1(b)
Now from the first check we guarantee that func2() was called as opposed to func2(y) or func2(b=y). In almost every case, the unique object sentinel is good enough to avoid having to truly guarantee how it was called, but it can be done.
But judging from the fact that you immediately return the result of func1, I see no reason why func2 even has default arguments. In the default call (func2()), that y is never used. So why is it there? Why don't you just use define func2(*a, **k) and pass them directly to func1?
Argument forwarding should be done with variadic arguments:
def func2(*args, **kwargs):
func1(*args, **kwargs)
Everything will just work, although introspection can suffer a bit.
If you need to sometimes not pass on an argument, you can remove an argument whenever:
del kwargs["name"]
An example:
def print_wrapper(*args, extrabig=False, **kwargs):
if extrabig:
args = [arg*2 for arg in args]
kwargs["sep"] = kwargs.get("sep", " ") * 2
print(*args, **kwargs)
print_wrapper(2, 4, 8, end="!!!\n")
#>>> 2 4 8!!!
print_wrapper(2, 4, 8, sep=", ", end="!!!\n")
#>>> 2, 4, 8!!!
print_wrapper(2, 4, 8, extrabig=True, end="!!!\n")
#>>> 4 8 16!!!
If you really don't want to do this (although you'd be wrong), you can use object to generate a unique sentinel.
# Bad! Won't let you print None
def optionally_print_one_thing(thing=None):
if thing is not None:
print(thing)
# Better
_no_argument = object()
def optionally_print_one_thing(thing=_no_argument):
if thing is not _no_argument:
print(thing)
What is your exact use case? func2 should be smart enough to only pass on the appropriate params to func1, and that should rely on the default values of any parameters.
The only time I have ever found it necessary to change how func2 calls func1 is when func1 is a c function with a screwy signature:
def func2(this, that, those=None):
if those is None:
return func1(this, that)
else:
return func1(this, that, those)

How to return a list in python?

I'm writing a program that takes a list of strings and return the length of of each string in a list.
def characters(nameLst):
nameLst = ["Dan","jason","may","cole","Zhan"]
outLst = []
for i in range(len(nameLst)):
outLst = outlst.append(len(nameLst))
return (outLst) # should return [3, 5, 3, 4, 4]
nameLst = ["Dan","jason","may","cole","Zhan"]
def main():
characters()
main()
every time I run the program I get an error:
characters() takes exactly 1 argument (0 given)
When you defined the method characters, you said that it takes one argument, called nameList, but when you called it inside of the main method, you don't give it any arguments, if you use
characters(nameList)
in your main method, this should fix your error.
Also, your code will not give you the lengths of the different strings in nameList, rather it will give you a list full of the length of nameList. With the given list, you would get
[5, 5, 5, 5, 5]
because the expression that you append to the list is len(nameList), when it should be len(i).
Finally, List.append() will append to the list, so you don't need to use an = sign. If you replace the line with:
outlst.append(len(nameLst[i]))
This should give you the correct output.
Edit: I just realized that you redefine nameLst inside of the characters function. It is not necessary to have nameLst both inside and outside of the function. Either define characters with no arguments and define nameLst inside of characters, or add nameLst as an argument and don't define it inside of the function.
every time I run the program I get an error: characters() takes exactly 1 argument (0 given)
Here is how you invoke characters():
characters()
Here is how it's defined:
def characters(nameLst):
Therefore, python expects you to call it like characters(names) instead of characters().
The likely fix is to move the nameLst contents to main's scope and pass it in (also your characters function does something different than you described. fix below):
def characters(nameLst):
outLst = []
for name in nameLst:
outlst.append(len(name))
return outLst # should return [3, 5, 3, 4, 4]
def main():
nameLst = ["Dan","jason","may","cole","Zhan"]
characters(nameLst)
if __name__ == '__main__':
main()
You've written characters(nameLst) with one parameter. Hence, when calling the function, be sure to pass the list in to the method.
Additionally, you will want to return(outLst) after your for loop - this way the entire list will be returned rather than just the first item.
Sidenote: Here is an easy implementation for your function:
def characters(nameLst):
return map(len, nameLst)
Example:
>>> characters(["Dan","jason","may","cole","Zhan"])
[3, 5, 3, 4, 4]
I guess, directly replacing characters(...) with map(len, ...) would be the better solution... ;-)
The error means exactly what it says. You declared the characters function to take a parameter called nameLst, but you called it with no parameters. Either change
def characters(nameLst):
to
def characters():
effectively making nameLst a local variable, or pass a list as a parameter when you call the function.
def characters(nameLst):
outLst = []
for i in range(len(nameLst)):
outLst = outlst.append(len(nameLst))
return (outLst)
def main():
nameLst = ["Dan", "jason", "may", "cole", "Zhan"]
characters(nameLst)
Furthermore, your function would be better written as a list comprehension:
def characters(nameLst):
return [len(name) for name in nameLst]
Note that you shouldn't expect any output from your program, since you never call print().
Your problem is not the return, but the call. You've defined your function to take one parameter, but you haven't given it one when you call it.
You define a function characters, which takes a single argument nameLst, so you need to call it with an argument, like this:
def main():
nameLst = ["Dan", "jason", "may", "cole", "Zhan"]
result = characters(nameLst) # Save the returned list on the variable result
print result # Print the result
main()

How to unpack a tuple while calling an external method in Python?

I call a method of an external library multiple times in my class like this:
class MyClass:
const_a = "a"
const_b = True
const_c = 1
def push(self, pushee):
with ExternalLibrary.open(self.const_a, self.const_b, self.const_c) as el:
el.push(pushee)
def pop(self):
with ExternalLibrary.open(self.const_a, self.const_b, self.const_c) as el:
return el.pop()
The lines containing the with statement are bugging me, because they require passing the the constants as arguments every time. I would like to store the arguments in a predefined data structure like a tuple and pass that to the external library.
You can do this:
args = (const_a, const_b, const_c)
ExternalLibrary.open(*args)
The * syntax unpacks an iterable (tuple, list, etc.) into individual arguments in a function call. There is also a ** syntax for unpacking a dictionary into keyword arguments:
kwargs = {'foo': 1, 'bar': 2}
func(**kwargs) # same as func(foo=1, bar=2)
You can also use both in the same call, like func(*args, **kwargs).

Categories

Resources