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.
Related
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)
This question already has answers here:
Bare asterisk in function parameters?
(6 answers)
Closed 4 years ago.
I was looking at the definition of the glob function and I noticed that the second argument was simply *.
def glob(pathname, *, recursive=False):
"""Return a list of paths matching a pathname pattern.
[...]
"""
return list(iglob(pathname, recursive=recursive))
What is the point of the *?
The * indicates the end of the positional arguments. Every argument after that can only be specified by keyword. This is defined in PEP 3102
>>> def foo1(a, b=None):
... print(a, b)
...
>>> def foo2(a, *, b=None):
... print(a, b)
...
>>> foo1(1, 2)
1 2
>>> foo2(1, 2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: foo1() takes 1 positional argument but 2 were given
>>> foo2(1, b=2)
1 2
All arguments after the * must have their name explicitly specified. For example, if you had this function:
def somefunction(a,*,b):
pass
You could write this:
somefunction(0, b=0)
but not this:
somefunction(0, 0)
Here's a simple function with a local function:
def raise_to(exp):
def raise_to_exp(x, exp):
return pow(x, exp)
return raise_to_exp
Now I expect the local function to close over exp, but somehow it doesn't. When I run this:
square = raise_to(2)
print(square.__closure__)
I get None. What am I missing?
There is no closure, no, because the inner function has it's own local exp variable; you gave it a parameter by that name. The parameter masks the name in the outer scope, so no closure is created for it. The function that is returned requires two arguments, and the argument to raise_to() is simply ignored:
>>> from inspect import signature
>>> def raise_to(exp):
... def raise_to_exp(x, exp):
... return pow(x, exp)
... return raise_to_exp
...
>>> signature(raise_to(2))
<Signature (x, exp)>
>>> square = raise_to(2)
>>> square(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: raise_to_exp() missing 1 required positional argument: 'exp'
>>> square(5, 3)
125
>>> raise_to('This is ignored, really')(5, 3)
125
Remove the exp parameter from the inner function if you wanted that to be taken from the outer function:
def raise_to(exp):
def raise_to_exp(x):
return pow(x, exp)
return raise_to_exp
Now exp is a closure:
>>> def raise_to(exp):
... def raise_to_exp(x):
... return pow(x, exp)
... return raise_to_exp
...
>>> raise_to(2).__closure__
(<cell at 0x11041a978: int object at 0x10d908ae0>,)
>>> raise_to.__code__.co_cellvars
('exp',)
The co_cellvars attribute on a code object gives you the names of any closed-over variable in the outer scope.
The function that is returned takes one argument, and the argument to raise_to() is now actually used:
>>> raise_to(2)(5)
25
>>> raise_to('Incorrect type for pow()')(5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in raise_to_exp
TypeError: unsupported operand type(s) for ** or pow(): 'int' and 'str'
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)
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.