How can I return function arguments args, kwargs in python - python

I would like to have a function the user specifies to return function arguments to another function.
def wrapper(func, arg_func, iters=10):
for i in range(iters):
args, kwargs = arg_func()
f(*args, **kwargs)
is this possible? Currently if I return a tuple from arg_func without any kwargs then it complains about too many arguments to unpack.
wrapper(func=print, arg_func=lambda: (random.uniform(0.0, 1.0),))
ValueError: not enough values to unpack (expected 2, got 1)

Your arg_func needs to return a sequence of two elements, the first of which is a sequence itself and the second is a dictionary.
Here is one way to invoke wrapper(). Note that the first element of the return value is a tuple (random.uniform(0.0, 1.0),) and the second is a dictionary {'flush':True}. These values are simply examples. use whatever sequence and dictionary are appropriate for your code.
wrapper(func=print,
arg_func=lambda: ((random.uniform(0.0, 1.0),), {'flush':True}))
Here is a another example. arg_func returns two positional arguments and no keyword arguments:
wrapper(func=print, arg_func=lambda: (("Hello","World"), {}))

You can do some extra handling. Something like
args, *more_args = arg_func()
kwargs = more_args[0] if more_args else {}
(Also if you're not using 3.x you'll need to do something like
x = arg_func()
a,k = (x[0] if len(x) else []), (x[1] if len(x) > 1 else {})

Related

How to specify arg position for functool partial()

As per manual, functools partial() is 'used for partial function application which “freezes” some portion of a function’s arguments and/or keywords resulting in a new object with a simplified signature.'
What's the best way to specify the positions of the arguments that one wishes to evaluate?
EDIT
Note as per comments, the function to be partially evaluated may contain named and unnamed arguments (these functions should be completely arbitrary and may be preexisting)
END EDIT
For example, consider:
def f(x,y,z):
return x + 2*y + 3*z
Then, using
from functools import partial
both
partial(f,4)(5,6)
and
partial(f,4,5)(6)
give 32.
But what if one wants to evaluate, say the third argument z or the first and third arguments x, and z?
Is there a convenient way to pass the position information to partial, using a decorator or a dict whose keys are the desired arg positions and the respective values are the arg values? eg to pass the x and z positions something like like this:
partial_dict(f,{0:4,2:6})(5)
No, partial is not designed to freeze positional arguments at non-sequential positions.
To achieve the desired behavior outlined in your question, you would have to come up with a wrapper function of your own like this:
def partial_positionals(func, positionals, **keywords):
def wrapper(*args, **kwargs):
arg = iter(args)
return func(*(positionals[i] if i in positionals else next(arg)
for i in range(len(args) + len(positionals))), **{**keywords, **kwargs})
return wrapper
so that:
def f(x, y, z):
return x + 2 * y + 3 * z
print(partial_positionals(f, {0: 4, 2: 6})(5))
outputs:
32
Simply use keyword arguments. Using your definition of f above,
>>> g = partial(f, z=10)
>>> g(2, 4)
40
>>> h = partial(f, y=4, z=10)
>>> h(2)
40
Note that once you use a keyword argument for a given parameter, you must use keyword arguments for all remaining arguments. For example, the following would not be valid:
>>> j = partial(f, x=2, z=10)
>>> j(4)
TypeError: f() got multiple values for argument 'x'
But continuing to use keyword arguments is:
>>> j = partial(f, x=2, z=10)
>>> j(y=4)
40
When you use functools.partial, you store the values of *args and **kwargs for later interpolation. When you later call the "partially applied" function, the implementation of functools.partial effectively adds the previously provided *args and **kwargs to the argument list at the front and end, respectively, as though you had inserted these argument-unpackings yourself. I.e., calling
h = partial(1, z=10)
f(4)
is roughly equivalent to writing
args = [1]
kwargs = {'z': 10}
f(*args, 4, **kwargs)
As such, the semantics of how you provide arguments to functools.partial is the same as how you would need to store arguments in the args and kwargs variables above such that the final call to f is sensible. For more information, take a look at the pseduo-implementation of functools.partial given in the functools module documentation
For easier usage, you can create a new object specifically to specify a positional argument that is to be skipped when sequentially listing values for positional arguments to be frozen with partial:
SKIP = object()
def partial_positionals(func, *positionals, **keywords):
def wrapper(*args, **kwargs):
arg = iter(args)
return func(*(*(next(arg) if i is SKIP else i for i in positionals), *arg),
**{**keywords, **kwargs})
return wrapper
so that:
def f(x, y, z):
return x + 2 * y + 3 * z
print(partial_positionals(f, 4, SKIP, 6)(5))
outputs:
32

Python methods and "switches"

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.

Python3.4 **kwargs, unexpected keyword argument

I have a dictionary of functions, all of which use 1 or 2 optional arguments. I want to iterate through this dictionary and pass both arguments to each iterated function, and have the functions that only need 1 argument to ignore the second. In these cases, however, I get an unexpected keyword argument error.
def getNumFrames(self, **kwargs):
return len(self.x)
def getConcentration(self, **kwargs):
if ((not gradient) or (not maxX)):
return 0
return (gradient / maxX) * self.x[0]
fields = {'numFrames': getNumFrames, 'concentration': getConcentration}
for field, fieldFunction in fields.items():
for track in tracks:
fieldFunction(object, maxX = 10, gradient = 2)
In this example, getConcentration would work, but getFrames would say maxX is an unexpected keyword.
*I edited my post to include my actual minimalist code, as was suggested.
A much better way to avoid all of this trouble is to use the following paradigm:
def func(obj, **kwargs):
return obj + kwargs.get(a, 0) + kwargs.get(b,0)
This makes use of the fact that kwargs is a dictionary consisting of the passed arguments and their values and get() performs lookup and returns a default value if the key does not exist.
Always access your named arguments through the dictionary kwargs. This makes your life much simpler.
I would go about this by creating a function like the one below with the second parameter optional.
func (obj, a = 0, b = 0):
return obj + a + b

functools.partial: TypeError: got multiple values for keyword argument

I am using the partial method from the functools module to map a function over a range of values:
def basic_rule(p,b,vx=1,**kwargs):
return (p / b) if vx != 0 else 0
def rule5(func,**kwargs):
vals = map(functools.partial(func,**kwargs), range(1,kwargs['b']+1))
return [x for i,x in enumerate(vals[:-1]) if x >= vals[i+1]] == []
rule5(basic_rule,p=100,b=10000)
Here is the error I get on line 5:
----> return map(functools.partial(func,**kwargs), range(1,kwargs['b']+1))
TypeError: basic_rule() got multiple values for keyword argument 'p'
It looks like functools.partial is trying to assign the range to the argument p, even though I have already assigned a value to it. I'm trying to assign the range to the value of vx. Any idea how I can make that happen?
EDIT: Added a little bit of extra context to the code. Essentially what I'd like test 5 to do is ensure that the result of the function given to it increases as vt goes up, so that `func(vt=1) < func(vt=2)... < func(vt=n).
functools.partial generates a partial that stores the arguments receiveids in 2 properties:
arguments stores positional arguments
keywords stores all keyword-based arguments
So the partial can call original function exactly as was intended. In other words, when you call the resulting partial with one argument (let's say, 1) it would be the same as:
original_func(1, **kwargs)
As your kwargs contains the first argument - and you're passing the "1" as a positional argument - you get the error.
I'm not sure if it's gonna work in this particular case, but one solution could be use inspect.getargspec to extract arguments from kwargs that can be passed as positional arguments. In this case, the rule5 function would be similar to:
def rule5(func, **kwargs):
# let's save 'b' argument because we'll need it in the range call
b = kwargs['b']
original_args = inspect.getargspec(func).args
# extract from kwargs all arguments that can be passed as positional
new_args = [kwargs.pop(key) for key in original_args if key in kwargs]
# construct the partial passing as much positional arguments as possible
fn = functools.partial(func, *new_args, **kwargs)
# now map will pass the range result as the next positional argument
vals = map(fn, range(1, b+1))
return [x for i,x in enumerate(vals[:-1]) if x >= vals[i+1]] == []

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