14

I am learning to use positional arguments in python and also trying to see how they work when mixed up with default arguments:-

def withPositionalArgs(ae=9,*args):
    print 'ae= ', ae
    print 'args = ', args


a=1
b=2
c=[10,20]

withPositionalArgs(a,b,c)

This gives me the output:

ae=  1
args =  (2, [10, 20])

As you can see, a is considered to be a value passed for ae, and b as well as c are considered to be the positional arguments.

So, I am now trying to assign 10 for ae while calling withPositionalArgs:

withPositionalArgs(ae=10,b,c)

But, I can not do it. I get the error:

SyntaxError: non-keyword arg after keyword arg

My question is:

Am I doing correctly? Is having default argument allowed or a good practice to use before positional arguments in python functions?

GodMan
  • 2,561
  • 2
  • 24
  • 40

3 Answers3

15

In Python2, you are not allowed to put arguments which have a default value before positional arguments.

The positional arguments must come first, then the arguments with default values (or, when calling the function, the keyword arguments), then *args, and then **kwargs.

This order is required for both the function definition and for function calls.

In Python3, the order has been relaxed. (For example, *args can come before a keyword argument in the function definition.) See PEP3102.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • @unubtu: Is is not allowed both in the definition as well as the function call? Or only at 1 place? – GodMan Sep 08 '12 at 15:58
  • This is the order one must use for both the definition and the function call. Though, in the function call, you can do things like `foo(c = 1, ae = 3, b = 2)`. That is, the order of the arguments can be jumbled by supplying them keyword arguments. – unutbu Sep 08 '12 at 16:00
  • It may seem silly to ask, but, Vaughn Cato's comment to my answer allows me to pass the argumenta as required, and python does not give any error. So, shall I modify your answer like: `In Python, In function calls, you are not allowed to put arguments which have a default parameter before positional arguments. But, you are allowed to do so in function definition` ? – GodMan Sep 08 '12 at 16:11
  • 1
    @GodMan The "default values" part is a red herring. – Andy Hayden Sep 08 '12 at 16:31
  • Commenting for @bal: When calling the function, keyword arguments come after *args and such to avoid the "Positional argument before keyword argument" error. – Red Jun 28 '20 at 01:11
4

I think we should make the distinction of default values vs. passing in arbitrary arguments/key-value pairs. The behaviour without default values is the same:

def f(ae,*args, **kwargs):
    print 'ae     = ', ae
    print 'args   = ', args
    print 'kwargs = ', kwargs

The way we have written this means that the first argument passed into f in the tuple args, that is f(1,2,3,a=1,b=2) (the sequence goes explicit arguments, *args, **kwargs.) Here: ae = 1, args = (2,3), kwargs = {'a': 1, 'b': 2}.

If we try to pass in f(1,2,3,a=1,ae=3) we are thrown a TypeError: f() got multiple values for keyword argument 'ae', since the value of ae is attempted to be changed twice.

.

One way around this is to only set ae when it is explicitly prescribed, we could (after the def line):

def g(*args, **kwargs):
    kwargs, kwargs_temp = {"ae": 9}, kwargs
    kwargs.update(kwargs_temp) #merge kwargs into default dictionary

and this time g(1,2,3,a=1,ae=3) sets args=(1,2,3), kwargs={a=1,ae=3}.

However, I suspect this is not best practice...

Andy Hayden
  • 359,921
  • 101
  • 625
  • 535
1

Python3 has relaxed ordering.

Now you can do something like:

def withPositionalArgs(*args, ae=9):
    print('ae=', ae)
    print('args =', args)
a=1
b=2
c=[10, 20]
withPositionalArgs(a, b, c, ae=7)