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
Answer from DSM on Stack Overflowpython - Can you have keyword arguments without supplying a default value? - Stack Overflow
python - Named parameter with no default? - Stack Overflow
python - Normal arguments vs. keyword arguments - Stack Overflow
python - Get a function argument's default value? - Stack Overflow
Videos
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.
Don't use defaults if the parameters are not optional.
You can still use those parameters as named parameters in the call; all parameters in a Python function are named:
def t(a, b):
return a+b
t(a=3, b=4)
Note that passing in the wrong count of parameters is always a runtime check, not a compile time check. As a dynamic language, it is impossible to know at compile time what actual object t will be when you call it.
However, if both a & b are not optional, then I need to check in the function in runtime, e.g.
No you don't. Just don't provide defaults:
def t(a, b):
return a + b
Trying to call it without the correct number arguments:
t()
Will lead to an error:
TypeError: t() takes exactly 2 arguments (0 given)
Or, trying to call it with incorrectly named arguments:
t(c=4)
Will also lead to an error:
TypeError: t() got an unexpected keyword argument 'c'
There are two related concepts, both called "keyword arguments".
On the calling side, which is what other commenters have mentioned, you have the ability to specify some function arguments by name. You have to mention them after all of the arguments without names (positional arguments), and there must be default values for any parameters which were not mentioned at all.
The other concept is on the function definition side: you can define a function that takes parameters by name -- and you don't even have to specify what those names are. These are pure keyword arguments, and can't be passed positionally. The syntax is
def my_function(arg1, arg2, **kwargs)
Any keyword arguments you pass into this function will be placed into a dictionary named kwargs. You can examine the keys of this dictionary at run-time, like this:
def my_function(**kwargs):
print str(kwargs)
my_function(a=12, b="abc")
{'a': 12, 'b': 'abc'}
There is one last language feature where the distinction is important. Consider the following function:
def foo(*positional, **keywords):
print "Positional:", positional
print "Keywords:", keywords
The *positional argument will store all of the positional arguments passed to foo(), with no limit to how many you can provide.
>>> foo('one', 'two', 'three')
Positional: ('one', 'two', 'three')
Keywords: {}
The **keywords argument will store any keyword arguments:
>>> foo(a='one', b='two', c='three')
Positional: ()
Keywords: {'a': 'one', 'c': 'three', 'b': 'two'}
And of course, you can use both at the same time:
>>> foo('one','two',c='three',d='four')
Positional: ('one', 'two')
Keywords: {'c': 'three', 'd': 'four'}
These features are rarely used, but occasionally they are very useful, and it's important to know which arguments are positional or keywords.
Python3.x
In a python3.x world, you should probably use a Signature object:
import inspect
def get_default_args(func):
signature = inspect.signature(func)
return {
k: v.default
for k, v in signature.parameters.items()
if v.default is not inspect.Parameter.empty
}
Python2.x (old answer)
The args/defaults can be combined as:
import inspect
a = inspect.getargspec(eat_dog)
zip(a.args[-len(a.defaults):],a.defaults)
Here a.args[-len(a.defaults):] are the arguments with defaults values and obviously a.defaults are the corresponding default values.
You could even pass the output of zip to the dict constructor and create a mapping suitable for keyword unpacking.
looking at the docs, this solution will only work on python2.6 or newer since I assume that inspect.getargspec returns a named tuple. Earlier versions returned a regular tuple, but it would be very easy to modify accordingly. Here's a version which works with older (and newer) versions:
import inspect
def get_default_args(func):
"""
returns a dictionary of arg_name:default_values for the input function
"""
args, varargs, keywords, defaults = inspect.getargspec(func)
return dict(zip(args[-len(defaults):], defaults))
Come to think of it:
return dict(zip(reversed(args), reversed(defaults)))
would also work and may be more intuitive to some people.
To those looking for a version to grab a specific default parameter with mgilson's answer.
value = signature(my_func).parameters['param_name'].default
Here's a full working version, done in Python 3.8.2
from inspect import signature
def my_func(a, b, c, param_name='apple'):
pass
value = signature(my_func).parameters['param_name'].default
print(value == 'apple') # True
Just call that functions as
foo(a=3, c=10)
Alternatively, you could use the following approach, which is e.g. necessary for lists, because default values are evaluated in the module scope:
def foo(a=None, b=None, c=None):
local_a = a or "default"
...
foo(a=42, b=None, c=16)
None then encodes to use the default, if None is no valid option.
Finally, you could just defined the defaults as "constants":
DEFAULT_A = 42
DEFAULT_B = "foo"
DEFAULT_C = 17
def foo(a=DEFAULT_A, b=DEFAULT_B, c=DEFAULT_C):
pass
foo(16, DEFAULT_B, 123)
But this is quite uncommon.
For you updated example, I would propose to use * and ** operators:
def create_foo(*args, **kwargs):
f = foo(*args, **kwargs)
create_foo(42, b="non-default")
See this question for explanations how these operators work.
If you don't use *args and **kwargs and subclassing there is no reason to explicitly call the defaults and as long as you are using only your own classes and you don't need mutual default arguments, you can just hand over the same values as the defaults - BUT if you are working with *args and **kwargs and the method to be called is from a super class, you might get problems with the MRO. (See this article for further information: https://www.python.org/download/releases/2.3/mro/)
The only way I know to prevent a(n at this moment unknown) super class to pass an argument to the class your calss extends is to call the method explicidly and than hand over only the arguments you want it to get.
I don't see another use case for an explicit default call.
Mixing parameters with and without default values can indeed be confusing. Parameters are the names used in the function definition, arguments are the values passed into a call.
When calling, Python will always fill all parameters from positional arguments, including names with default values. size is just another parameter here, even though it has a default value. You can also use name=value syntax in a call to assign an argument value to a specific parameter (whether or not they have a default value). But you can't tell Python not to assign something to size, not with your current function definition, because everything before the *toppings parameter is always going to be a regular positional parameter.
The *toppings parameter will only capture any positional arguments after all the other parameters have received values. So 'onion' is assigned to size, and the remainder is assigned to *toppings.
In Python 3, you can make size a keyword-only parameter, by placing them as name=default after the *toppings name, or an empty *:
def make_pizza(*toppings, size=15):
Now size can only be set from a call with size=<new value> keyword argument syntax.
In Python 2, you can only capture such keyword arguments with a **kwargs catch-all parameter, after which you need to look into that dictionary for your size:
def make_pizza(*toppings, **kwargs):
size = kwargs.get('size', 15) # set a default if missing
In both cases, you have to remember to explicitly name size, and put such explicitly named keyword arguments after the positional arguments:
make_pizza('ham', 'extra meat', 'sweet con', 'pepperoni', size=17)
There's a fundamental problem with your approach: It's ambiguous because how do you know if you intended it as size or as topping? Python can't do that so you need to find an alternative.
Well, you could simply remove size from the argument list and interpret the first *toppings argument as size if it's an integer:
def make_pizza(*toppings):
if toppings and isinstance(toppings[0], int):
size, toppings = toppings[0], toppings[1:]
else:
size = 15
print("\nMaking a {}-inch pizza with the following toppings: ".format(size))
for topping in toppings:
print("- " + topping)
However that will fail for cases where a simple type check isn't possible. Maybe the better approach would be to make toppings a normal argument and size an optional argument:
def make_pizza(toppings, size=15):
print("\nMaking a {}-inch pizza with the following toppings: ".format(size))
for topping in toppings:
print("- " + topping)
However then you need to pass in a sequence for toppings and it changes the order of toppings and size but it's probably a lot cleaner.
make_pizza(['onion','shrimp','goda cheese','mushroom'])
make_pizza(['ham','extra meat','sweet con','pepperoni'], 17)
You could also keep the toppings as arbitary positional arguments and make size a keyword-only parameter with default (Python3-only):
def make_pizza(*toppings, size=15):
print("\nMaking a {}-inch pizza with the following toppings: ".format(size))
for topping in toppings:
print("- " + topping)
make_pizza('onion','shrimp','goda cheese','mushroom')
make_pizza('ham','extra meat','sweet con','pepperoni', size=17)