Pass arguments only when needed - python

As an example I have a function f and a variable a=1.
Sometimes f needs a and I want to call f(a=a) and other times f has no arguments. How can I deal with that?
When I try to call f(a=a) (expecting that it will silently ignore the case where a is not used in f), I get the following TypeError:
f got an unexpected keyword argument 'a'
Edit
I want to implement a function g such that, given:
def f1(a):
return a
def f2():
return 1
we have:
g(f1, a) == a
g(f2, a) == 1

It seems you want to inspect a function's arguments.
For that, use the inspect module:
import inspect
def g(somefunc, *params):
num_args = len(inspect.getargspec(somefunc).args)
return somefunc(*params[:num_args])
Testing, with your functions:
def f1(a):
return a
def f2():
return 1
>>> print(g(f1, 3))
3
>>> print(g(f2, 3))
1

use keyword dictionary in f:
def f(**kwargs):
if 'a' in kwargs:
print("a passed with value {}".format(kwargs['a']))
else:
print('a not passed')
f(a=12)
f()
prints:
a passed with value 12
a not passed
it makes argument testing/retrieving completely manual. You can do anything you want, and it can be generalized to several arguments.
This also forbids to pass arguments as positional like f(12) which is probably a good thing in your case.
>>> f(12)
Traceback (most recent call last):
TypeError: f() takes 0 positional arguments but 1 was given
The drawback is that the caller cannot rely on parameter names to know what to pass. Either create a docstring, or people will have to read the code/guess...
Now to apply that to your edit, which kind of changes the problem I must say. So f2 cannot be changed, in that cas the work must be done in the wrapper function:
def f1(a):
return a
def f2():
return 1
def g(f,arg):
try:
return f(a=arg)
except TypeError:
return f()
a=12
print(g(f1, a))
print(g(f2, a))
prints:
12
1
so "better ask forgiveness than permission": if we get a TypeError (raised when the parameter isn't known by the function), we just call the function without the parameter. The drawback if that if the first function returns a TypeError, we cannot know if it was the parameters or inside the function.
A workaround would be to inspect the error message and only call the function without parameters if the error message is about parameters:
def g(f,arg):
try:
return f(a=arg)
except TypeError as e:
if "unexpected keyword" in str(e):
return f()
else:
raise e # another TypeError, let it pass

Simply specify the default argument in the function def line...
def f(a=1):
return(a)
print(f()) #returns 1
print(f(a = 2)) #returns 2
For your next part (as you seemed to have switched your question with edits...
def g(f, a):
return(f(a))

Related

How to correctly use functions in a class?

I'm having a difficulty implementing functions in the class ArrayQ. The task is to create a class and have three methods in it, enqueue, dequeue and isEmpty. In "self.array.append(self.array)" I'm not really sure if it's supposed to be array,self.array,self or something else in the parenthesis. The basictest() function below was given to us to controll that our class works, and it doesn't at the moment.
from array import array
class ArrayQ:
def __init__(self,array):
self.array = array
def enqueue(self):
self.array.append(self.array)
def dequeue(self):
self.array.pop(0)
def isEmpty(self):
if not self.array:
print("queue is empty")
#print(ArrayQ)
lista = []
def basictest():
q = ArrayQ(lista)
q.enqueue(1)
q.enqueue(2)
x = q.dequeue()
y = q.dequeue()
if (x == 1 and y == 2):
print("test OK")
else:
print("FAILED expexted x=1 and y=2 but got x =", x, " y =", y)
basictest()
I get the following error message:
Traceback (most recent call last):
File "d1.py", line 31, in <module>
basictest()
File "d1.py", line 22, in basictest
q.enqueue(1)
TypeError: enqueue() takes 1 positional
argument but 2 were given
So could anybody please guide me towards how I should solve this problem so I could make this code work? Among the presumably several errors in this code, why can't I use "q.enqueue(1)" to alter my list using the function, or method, written in the class?
Your problem is here.
def enqueue(self):
self.array.append(self.array)
This method takes one param: self. It's a reference of that object, so, unless you've got a classmethod your class methods must have a self param as first parameter.
Now, your basictest() function calls a q.enqueue(1) but your object q is a ArrayQ and its enqueue function has only one parameter: self. It's implicitly, you cannot use 1 as self.
So, edit enqueue in this way:
def enqueue(self, value):
self.array.append(value)
First of all, there is an incorrect indentation in the class "ArrayQ". And second, there is only one default argument is passed in the "enqueue" method definition. When you create an instance of an object, the object itself is passed as a default argument(self). In "q.enqueue(1)" you are actually passing two parameters, first is self and the second is "1".

Python decorator function call depth, structured output

I am printing inputs and outputs of functions, but this results in hard to read logs, especially with too many nested function calls (not recursive).
Example:
Calling test1()
Calling test2()
Calling test3()
Calling test4()
'test4' returned None
'test3' returned None
'test2' returned None
'test1' returned None
I would like to see the output like this:
Calling test1()
Calling test2()
Calling test3()
Calling test4()
'test4' returned None
'test3' returned None
'test2' returned None
'test1' returned None
If I could keep a call count and transfer it to next calls by incrementing it each time, I could achieve this. Then I could even color the output based on the value of this count and generate easier to read logs.
Is there a way I could keep the depth of current call inside the iterator?
For reference, here is the decorator:
def trace(func):
"""Print the function signature and return value"""
#functools.wraps(func)
def wrapper_debug(*args, **kwargs):
args_repr = [repr(a) for a in args] # 1
kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()] # 2
signature = ", ".join(args_repr + kwargs_repr) # 3
print(start + f"Calling {func.__name__}({signature})" + end)
value = func(*args, **kwargs)
print(f"{func.__name__!r} returned {value!r}") # 4
return value
return wrapper_debug
Python knows your call depth, anytime.
You can also print the traceback of where your currently are, anytime.
No need to keep it in a separate variable.
So here you go:
import traceback
len(traceback.format_stack())
Up to you to make the indent and to fix any off-by one errors.

Returning multiple objects and using them as arguments

I have a function which goes something like this:
def do_something(lis):
do something
return lis[0], lis[1]
and another function which needs to take those two return objects as arguments:
def other_function(arg1, arg2):
pass
I have tried:
other_function(do_something(lis))
but incurred this error:
TypeError: other_function() missing 1 required positional argument: 'arg2'
You need to unpack those arguments when calling other_function.
other_function(*do_something(lis))
Based on the error message, it looks like your other function is defined (and should be defined as)
def other_function(arg1, arg2):
pass
So, when you return from do_something, you are actually returning a tuple containing (lis[0], lis[1]). So when you originally called other_function, you passing a single tuple, when your other_function was still expecting a second argument.
You can see this if you break it down a bit further. Below is a breakdown of how the returns look like when handled differently, a reproduction of your error and a demo of the solution:
Returning in to a single variable will return a tuple of the result:
>>> def foo():
... lis = range(10)
... return lis[1], lis[2]
...
>>> result = foo()
>>> result
(1, 2)
Returning in to two variables, unpacks in to each var:
>>> res1, res2 = foo()
>>> res1
1
>>> res2
2
Trying to call other_function with result which now only holds a tuple of your result:
>>> def other_function(arg1, arg2):
... print(arg1, arg2)
...
>>> other_function(result)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: other_function() missing 1 required positional argument: 'arg2'
Calling other_function with res1, res2 that holds each value of the return from foo:
>>> other_function(res1, res2)
1 2
Using result (your tuple result) and unpacking in your function call to other_function:
>>> other_function(*result)
1 2
You can do it like this:
other_function(*do_something(list))
The * char will expand the tuple returned by do_something.
Your do_something function is actually returning a tuple which contains several values but is only one value itself.
See the doc for more details.

Force function caller decide which default value is used

Lets say I have the following function:
def myFun(a=None, b=None):
print a, b
Now I call the function:
myFun("hi") # Case 1
>>>hi None
myFun(b="hi") # Case 2
>>>None hi
myFun(a="hi") # Case 3
>>>hi None
Is there a way to throw an exception if the function caller did not decide which variable the value "hi" is assigned to? That means I would like to have an exception in the 1st case but not in the 2nd and 3rd case. I use python 2.7.
In Python3 you can specify keyword-only arguments:
def myFun(*, a=None, b=None):
print(a, b)
myFun('hi')
raises
TypeError: myFun() takes 0 positional arguments but 1 was given
In Python2, as Ricardo Silveira points out you could use **kwargs to force all arguments to be keyword arguments.
def myFun(**kwargs):
a, b = kwargs.get('a'), kwargs.get('b')
print(a, b)
myFun('hi')
# TypeError: myFun() takes exactly 0 arguments (1 given)
def my_fun(**kwargs):
a = kwargs.get("a", None)
b = kwargs.get("b", None)
# have your code here...
Then: my_fun("hi") shouldn't work...
You can actually allow the user to provide any arguments they'd like by using **kwargs.
def myFun(**kwargs):
print kwargs[a]
print kwargs[b]
This will cause an error if a or b aren't defined, but they're not very helpful.
You can make your own errors by checking if the value exists. For example:
def myFun(**kwargs):
if not kwargs.get(a):
raise Exception('a is not here!')
if not kwargs.get(b):
raise Exception ('b is not here!')
print kwargs[a], kwargs[b]

Python - Passing a function into another function [duplicate]

This question already has answers here:
Python function as a function argument?
(10 answers)
Closed last month.
I am solving a puzzle using python and depending on which puzzle I am solving I will have to use a special set of rules. How can I pass a function into another function in Python?
Example
def Game(listA, listB, rules):
if rules == True:
do...
else:
do...
def Rule1(v):
if "variable_name1" in v:
return False
elif "variable_name2" in v:
return False
else:
return True
def Rule2(v):
if "variable_name3" and "variable_name4" in v:
return False
elif "variable_name4" and variable_name1 in v:
return False
else:
return True
This is just a pseudo code and therefore not specific but I get the code to compile but I need to know how to call the function Game and whether it's correctly defined since rules will be switched for either Rule1(v) or Rule2(v).
Just pass it in like any other parameter:
def a(x):
return "a(%s)" % (x,)
def b(f,x):
return f(x)
print b(a,10)
Treat function as variable in your program so you can just pass them to other functions easily:
def test ():
print "test was invoked"
def invoker(func):
func()
invoker(test) # prints test was invoked
For passing both a function, and any arguments to the function:
from typing import Callable
def looper(fn: Callable, n:int, *args, **kwargs):
"""
Call a function `n` times
Parameters
----------
fn: Callable
Function to be called.
n: int
Number of times to call `func`.
*args
Positional arguments to be passed to `func`.
**kwargs
Keyword arguments to be passed to `func`.
Example
-------
>>> def foo(a:Union[float, int], b:Union[float, int]):
... '''The function to pass'''
... print(a+b)
>>> looper(foo, 3, 2, b=4)
6
6
6
"""
for i in range(n):
fn(*args, **kwargs)
Depending on what you are doing, it could make sense to define a decorator, or perhaps use functools.partial.
Just pass it in, like this:
Game(list_a, list_b, Rule1)
and then your Game function could look something like this (still pseudocode):
def Game(listA, listB, rules=None):
if rules:
# do something useful
# ...
result = rules(variable) # this is how you can call your rule
else:
# do something useful without rules
A function name can become a variable name (and thus be passed as an argument) by dropping the parentheses. A variable name can become a function name by adding the parentheses.
In your example, equate the variable rules to one of your functions, leaving off the parentheses and the mention of the argument. Then in your game() function, invoke rules( v ) with the parentheses and the v parameter.
if puzzle == type1:
rules = Rule1
else:
rules = Rule2
def Game(listA, listB, rules):
if rules( v ) == True:
do...
else:
do...

Categories

Resources