is to possible to "pack" arguments in python? I have the following functions in the library, that I can't change (simplified):
def g(a,b=2):
print a,b
def f(arg):
g(arg)
I can do
o={'a':10,'b':20}
g(**o)
10 20
but can I/how do I pass this through f?
That's what I don't want:
f(**o)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() got an unexpected keyword argument 'a'
f(o)
{'a': 10, 'b': 20} 2
f has to accept arbitrary (positional and) keyword arguments:
def f(*args, **kwargs):
g(*args, **kwargs)
If you don't want f to accept positional arguments, leave out the *args part.
Related
I am trying to understand decorators. I want to define a decorator that can handle any arbitrary argument. I am trying the following:
def a_decorator_passing_arbitrary_arguments(function_to_decorate):
def a_wrapper_accepting_arbitrary_arguments(*args,**kwargs):
print('The positional arguments are', args)
print('The keyword arguments are', kwargs)
function_to_decorate(*args)
return a_wrapper_accepting_arbitrary_arguments
This is based on this tutorial and it supposedly handles any type of argument. However when I pass only keyword arguments I get the following output with function f(a,b,c):
#a_decorator_passing_arbitrary_arguments
def f(a,b,c):
print("The arguments here are the following: {0}, {1}, {2}.".format(a,b,c))
f(a='ali', b='emma', c='t')
The output:
The positional arguments are ()
The keyword arguments are {'a': 'ali', 'b': 'emma', 'c': 't'}
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-cc5ee8d7120a> in <module>
----> 1 f(a='ali', b='emma', c='t')
<ipython-input-1-af03800e2abd> in a_wrapper_accepting_arbitrary_arguments(*args, **kwargs)
3 print('The positional arguments are', args)
4 print('The keyword arguments are', kwargs)
----> 5 function_to_decorate(*args)
6 return a_wrapper_accepting_arbitrary_arguments
TypeError: f() missing 3 required positional arguments: 'a', 'b', and 'c'
How can I avoid getting this error in the case when all the variables are passed as keyword arguments?
You are currently only passing on the positional arguments, not the keyword arguments as well, to the wrapped function.
def a_decorator_passing_arbitrary_arguments(function_to_decorate):
def a_wrapper_accepting_arbitrary_arguments(*args,**kwargs):
print('The positional arguments are', args)
print('The keyword arguments are', kwargs)
# function_to_decorate(*args) # Wrong
return function_to_decorate(*args, **kwargs) # Right
return a_wrapper_accepting_arbitrary_arguments
(You should also return whatever function_to_decorate returns from the wrapper.)
def f(a, *arguments):
for arg in arguments:
print(arg)
return arg
f(5)
Error:
Traceback (most recent call last):
File "test.py", line 6, in <module>
f(5)
File "test.py", line 5, in f
return arg
UnboundLocalError: local variable 'arg' referenced before assignment
if you change the function f() like this
def f(a, *arguments):
for arg in range(1,4):
print(arg)
return arg
f(5)
the output is this:
1
2
3
now arg is global in the function. Why did this happen?
def f(a, *arguments):
for arg in arguments:
print(arg)
return arg
f(5)
Since *arguments is empty, you never enter the loop. Thus, when you reach return(arg) the variable is still undefined. Try calling it with f(5, [1, 2, 3]) and you'll see something more like your expectations.
#Prune's answer is apt for your question. You may still want to return something even if arguments is empty. You can do so by adding a check for arguments length.
def f(a, *arguments):
if len(arguments):
for arg in arguments:
print(arg)
return arg # will return last element in arguments always
return -1 # will return default value here
f(5)
Suppose I want to define a meta-function that accept a function as an argument and returns a new modified function. Something like
metaf(f) -> f**2
So whatever f would result, metaf would result with the answer to the power of two (and if the result cannot be raised to the power of 2, so be it. Raise an error).
Currently the way I've found to do that requires explicit reference to the argument of f in the definition of metaf, i.e. define
metaf = lambda f, x : f(x)**2
and then
mynewf = lambda x : metaf(f, x)
This works, but I wonder if it will hold for complex argument functions where there could be many variation to the input argument.
So I'm wondering if you can suggest a different way, especially one that does not require specifying the argument in metaf definition.
Edit: Both answers below were helpful. Just for reference, following the answers I've also realized the way to define metaf using lambda expression. Slightly cumbersome, but might still be worth noting:
metaf = lambda func: lambda *args, **kwargs : func(*args, **kwargs)**2
I believe this is what you are after:
def metaf(f):
def func(*r, **kw):
return f(*r, **kw) ** 2
return func
And now let's define some f...
def f(a, b):
return a + b
And here is converting it:
mynewf = metaf(f)
Try it:
In [148]: f(10, 20)
Out[148]: 30
In [149]: mynewf(10, b=20)
Out[149]: 900
Please note the use of both normal argument and keyword argument in the useage of mynewf. I works as you would expect.
You should be able to use *args and **kwargs to gobble up all other arguments and pass them on like so:
def squarer(f):
return lambda *args, **kwargs: f(*args, **kwargs)**2
>>> squarer(lambda x: x+1)(3)
16
>>> squarer(lambda x: x+1)(4)
25
>>> squarer(lambda x,y: x+1)(4)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in <lambda>
TypeError: <lambda>() takes exactly 2 arguments (1 given)
>>> squarer(lambda x,y=1: x+y)(4)
25
>>> squarer(lambda x,y=1: x+y)(4,2)
36
I defined a function which takes 2 arguments. When I call the function, I get an error saying not enough argument:
>>> def fib(self, a,b):
... self.i=a, self.j=b
... print self.i+self.j
...
>>> fib(4,8)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: fib() takes exactly 3 arguments (2 given)
>>> fib(4,8,9)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in fib
AttributeError: 'int' object has no attribute 'i'
I passed with both 2 and 3 arguments. What should be the third argument?
I am assuming you don't understand self very well in python. Its heading towards OOP (Object oriented programming).
non-OOP approach (doing the same thing with static methods)
def fib(a,b):
print a+b
fib(4,8)
OOP approach
class Test():
i = 0
j = 0
def fib(self, a,b):
self.i=a
self.j=b
print self.i+self.j
t = Test() # create an object of Test class
t.fib(2, 3) # make the function call
NOTE : python considers a function to be a static function if it does not have the keyword self as the first parameter
You function has 3 arguments: self, a and b.
self is traditionally used for methods.
You write (simplified example):
class A:
def multiply(self, b): # method called with one argument
return 2 * b
a = A()
a.multiply(3)
or
def multiply(b): # this is a function with one argument
return 2*b
mutiply(3)
I am used to having function/method definitions like so in Python:
def my_function(arg1=None , arg2='default'):
... do stuff here
If I don't supply arg1 (or arg2), then the default value of None (or 'default') is assigned.
Can I specify keyword arguments like this, but without a default value? I would expect it to raise an error if the argument was not supplied.
You can in modern Python (3, that is):
>>> def func(*, name1, name2):
... print(name1, name2)
...
>>> func()
Traceback (most recent call last):
File "<ipython-input-5-08a2da4138f6>", line 1, in <module>
func()
TypeError: func() missing 2 required keyword-only arguments: 'name1' and 'name2'
>>> func("Fred", "Bob")
Traceback (most recent call last):
File "<ipython-input-7-14386ea74437>", line 1, in <module>
func("Fred", "Bob")
TypeError: func() takes 0 positional arguments but 2 were given
>>> func(name1="Fred", name2="Bob")
Fred Bob
Any argument can be given as with a keyword expression, whether or not it has a default:
def foo(a, b):
return a - b
foo(2, 1) # Returns 1
foo(a=2, b=1) # Returns 1
foo(b=2, a=1) # Returns -1
foo() # Raises an error
If you want to force the arguments to be keyword-only, then see DSM's answer, but that didn't seem like what you were really asking.