Python, Default Keyword Arguments After Variable Length Positional Arguments

Python, default keyword arguments after variable length positional arguments

It does work, but only in Python 3. See PEP 3102. From glancing over the "what's new" documents, it seems that there is no 2.x backport, so you're out of luck. You'll have to accept any keyword arguments (**kwargs) and manually parse it. You can use d.get(k, default) to either get d[k] or default if that's not there. To remove an argument from kwargs, e.g. before calling a super class' method, use d.pop.


Note that in def get(self, *args, raw=False, vars=None):, the raw=False and vars=None have nothing to do with keyword arguments. Those are default argument values. Arguments with a default value may be passed positionally, and arguments without a default value may be passed by keyword:

def f(a=1): pass
f(2) # works, passing a positionally
def f(a): pass
f(a=2) # works, passing a by keyword

Similarly, keyword-only arguments are not required to have a default value. Coming after the *args argument is what marks them as keyword-only, not the presence of a default value:

def f(*args, a): pass
# a is a mandatory, keyword-only argument

Does Python support Default Keyword and Default Variable Length Arguments?

No, *args and **kwargs are the catch-all arguments that receive any arguments for which there are no explicit arguments instead. Specifying defaults defeats their purpose. Instead, your example defaults can be handled with existing syntax, there is no need to complicate the function definition syntax here.

You can cover your defaults by specifying normal keyword arguments:

def foo(pos0, pos1='v', a=20, *args, **kwargs):
pass

I replaced your (1, 'v') 'default' with another keyword argument, because you can provide values for keyword arguments with a positional parameter; foo(42, 'spam') will set pos1 to 'spam'.

In addition, you can always do further processing inside the function you define; the args value is a tuple, just test for length to check if you need to use a default for missing values. kwargs is a dictionary, just use kwargs.get(key, default) or kwargs.pop(key, default) or if key not in kwargs: or any of the other dict methods to handle missing keys.

How to pass default & variable length arguments together in python?

make sex a **kwargs in your function. Inside your function, check whether user has passed sex as key argument or not. If user hasn't passed anything than continue with default value of sex.

your function will look like this:

def anyFunc(name, age, *cap_letters,**sexKwarg):
print "name ", name
print "age ", age
sex = sexKwarg.get('sex', 'M')
print "sex ", sex
for s in cap_letters:
print "capital letter: ", s

Usage:

anyFunc("Mona", 45, *('H', 'K', 'L'), sex = 'F')
anyFunc("Mona", 45, *('H', 'K', 'L')) ##use default sex

Default argument followed by variable length arguments (Python)

set_football_team(name, coach, *players, **formations)

When the function is called as above with coach is passed as argument, coach is assigned the value which is passed in argument for function set_football_team.

set_football_team(name,*players, **formations)

When the function is called as above when no coach argument is passed explicitly, coach is primarily being assigned first value of *players, the remaining values of players are passed on to players,which is why you notice only 4 players in players while the 0th element in the list got assigned to coach.

Can you reference positional arguments when setting default values for keyword arguments?

No, this can't be done, but you've basically solved the issue in the usual way.
Usually in this case people will set the default values to None and do the same thing (just because this is less wierd with typing and thus linting - the function accepts either an array or nothing rather than an array or a string.)

In terms of usability, you can tell the user what the default value is effectively doing in the doc script.

def iterate(list_a, list_b=None):
'list_b defaults to zero array of size a'
if not list_b:
list_b = np.zeros((len(list_a),), dtype=int)

for a, b in zip(list_a, list_b):
# do something with a and b

The reason it's not possible is because the list_a variable hasn't been created yet when you call it in the same line, since the python interpreter goes line by line.

Set keyword argument's default value as length of string

Let's categorize parameters into 3 types:

  • required (positional) parameter (like substring)
  • optional parameter (like start, end), often with None as type-less placeholder
  • parameter with default argument values (like start, end)

See also:

  • "Least Astonishment" and the Mutable Default Argument
  • Effbot/Fredrik Lundh (July 17, 2008): Default Parameter Values in Python, via Wayback archive, using None as default value

Use slicing and default for end parameter

See also the runnable demo on IDEone:

class StringUtil:

def __init__(self, s):
self.string = s


def count(self, substring, start=0, end=-1): # end index/slice: -1 for the last, instead len(self.substring) - 1
self.substring = substring
self.start = start
self.end = end
self.queue = 0
self.counter = 0

for a in self.string[start:]:
if a == self.substring[0]:
self.queue += 1
if a == self.substring[end] and self.queue != 0: # -1 for the last, instead len(self.substring) - 1
self.queue -= 1
self.counter += 1

return self.counter

sub = "o*o"
s = "Hell" + sub + " W" + sub + "rld"
cnt = StringUtil(s).count(sub)
print(f" counted '{sub}' in '{s}' times: {cnt}")

Why Does Python Allow *args After Keyword Arguments?

Given:

def foo(a, b=2, *args, **kwargs): pass

b is not a keyword-only parameter - it is just a parameter for which arguments can be positional or named, but have a default value. It is not possible to pass any value into args and omit passing b or passing b out of order in the signature you suggest.

This signature makes sense and is quite unambiguous - you can pass from 0 to n positional arguments, but if you pass 2 or more, the second argument is assigned to "b", and end of story.
If you pass 0 positional arguments, you can still assign values to "a" or "b" as named arguments, but trying anything like: foo(0, 1, 2, a=3, b=4) will fail as more than one value is attempted to be passed to both parameters.

Where as in:

def foo(a, *args, b=2, **kwargs): pass

it is also an unambiguous situation: the first positional argument goes to "a", the others go to "args", and you can only pass a value to "b" as a named argument.

The new / syntax in signature definition coming with Python 3.8 gives more flexibility to this, allowing one to require that "a" and "b" are passed as positional-only arguments. Again, there is no ambiguity:

def foo(a, b=2, /, *args, **kwargs): pass

A curious thing on this new syntax: one is allowed to pass named arguments to "a" and "b", but the named arguments will come up as key/value pairs inside "kwargs" - while the local variables "a" and "b" will be assigned the positional only arguments:

def foo(a, b=2, /, *args, **kwargs): 
print(a, b, args, kwargs)

...

In [9]: foo(1, 2, a=3, b=4)
1 2 () {'a': 3, 'b': 4}

Whereas with the traditional syntax you ask about - def foo(a, b=2, *args, **kwargs): - one gets a TypeError if that is tried:

In [11]: foo(1,2, a=3, b=4)                                                                                                                                               
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-11-d002c7717dba> in <module>
----> 1 foo(1,2, a=3, b=4)

TypeError: foo() got multiple values for argument 'a'

Positional argument vs keyword argument

That text you quote seems to be confused about two totally different things:

  • Positional and keyword arguments are a feature of calls to a function (see Python reference section 5.3.4 Calls).
  • Default values are a feature of function definitions, as per section 7.6 Function definitions

I suspect the people who put together that course-ware weren't totally familiar with Python :-) Hence that link you provide is not a very good quality one.


In your call to your function, you're using the "keyword argument" feature (where the argument is named rather than relying on its position). Without that, values are bound to names based on order alone. So, in this example, the two calls below are equivalent:

def process_a_and_b(a, b):
blah_blah_blah()

process_a_and_b(1, 2)
process_a_and_b(b=2, a=1)

By further way of example, refer to the following definition and calls:

def fn(a, b, c=1):        # a/b required, c optional.
return a * b + c

print(fn(1, 2)) # returns 3, positional and default.
print(fn(1, 2, 3)) # returns 5, positional.
print(fn(c=5, b=2, a=2)) # returns 9, named.
print(fn(b=2, a=2)) # returns 5, named and default.
print(fn(5, c=2, b=1)) # returns 7, positional and named.
print(fn(8, b=0)) # returns 1, positional, named and default.

python 2.7, functions with positional, keyword arguments as well as variable number of arguments (arbitrary argument lists)

This is a Python 3 feature, introduced in PEP 3102. (Python, default keyword arguments after variable length positional arguments)

In Python 2, you will have to extract the keyword arguments from **kwargs:

def foo(positional arguments, *args, **kwargs):
# for a required positional argument
try:
kwarg = kwargs.pop('kwarg')
except KeyError as e:
raise TypeError('Required positional argument not found', e)

# or for an optional positional argument
kwarg = kwargs.pop('kwarg', None)

if kwargs:
raise TypeError('Unexpected positional arguments', kwargs)


Related Topics



Leave a reply



Submit