Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python positional args and keyword args

I am reading the source codes of mercurial, and found such a func def in commands.py:

def import_(ui, repo, patch1=None, *patches, **opts):
    ...

in python, postional args must be put ahead of keyword args. But here, patch1 is a keyword argument, followed by a positional argument *patches. why is this OK?

like image 634
guoqiao Avatar asked Dec 13 '11 08:12

guoqiao


4 Answers

Just have a look into the PEP 3102 also it seems its somehow related to this.

To summarize, patches and opts are there to accept variable arguments but the later is to accept keyword arguments. The keyword arguments are passed as a dictionary where as the variable positional arguments would be wrapped as tuples .

From your example

def import_(ui, repo, patch1=None, *patches, **opts):

Any positional parameters after u1,repo and patch1 would be wrapped as tuples in patches. Any keyword arguments following the variable positional arguments would be wrapped as Dictionary objects through opts.

Another important thing is that, the onus lies with the caller to ensure that the condition non-keyword arg after keyword arg is not violated.

So something that violates this would raise a syntax error..

For example

Calls like

 import_(1,2,3,test="test")
 import_(1,2,3,4,test="test")
 import_(1,2,3,4,5)
 import_(1,2,patch1=3,test="test")

are valid, but

import_(1,2,3,patch1=4,5)

would raise a syntax error SyntaxError: non-keyword arg after keyword arg

In the first valid case import_(1,2,3,test="test")

u1 = 1, repo = 2, patch1 = 3, patches = () and opts={"test":"test"}

In the second valid case import_(1,2,3,patch1=4,test="test")

u1 = 1, repo = 2, patch1 = 3 , patches = (4) and opts={"test":"test"}

In the third valid case import_(1,2,3,4,5)

u1 = 1, repo = 2, patch1 = 3 , patches=(4,5), and opts={}

In the fourth valid case import_(1,2,patch1=3,test="test")

u1 = 1, repo = 2, patch1 = 3 , patches=(), and opts={"test":"test"}
you can use patch1 as a keywords argument but doing so you cannot wrap any variable positional arguments within patches
like image 186
Abhijit Avatar answered Oct 17 '22 04:10

Abhijit


Probably you are confusing the function definition and the function call syntax a bit.

patch1 is not a keyword arg, it's a positional arg with a default argument value assigned.

*patches is an argument list, and not a positional argument.


Please have a look at this section from the official tutorials:

  • http://docs.python.org/tutorial/controlflow.html#more-on-defining-functions

Now let me summarize the main points using this function as an example:

def f1(a1, a2, a3=None, *args, **kwargs):
  print a1, a2, a3, args, kwargs

Function Definition

You have a number of arguments that are defined explicitly by name (a1, a2 and a3), of which a3 will be default initialized by None if not supplied during call. The arguments a1 and a2 need to be supplied in any valid call of that function.

The function may be called with additional arguments that will appear in the dictionary kwargs (when supplied by keyword) or in the list args (when not supplied by keyword). If args and kwargs would not be present in the function definition, the caller would not be allowed to add more arguments besides those explicitly named in the function definition for the function call.

In the function definition you need to specify the explicit arguments without default initializer first, second the explicit arguments with default initializer, third the argument list, lastly the keyword argument dictionary.

Function Call

There are various ways to call the function. For example will the following calls produce equal results:

f1(1, 2)       # pass a1 and a2 as positional arguments
f1(a2=2, a1=1) # pass a1 and a2 as keyword arguments
f1(1, a2=2)    # pass a1 as positional argument, a2 as keyword argument

That is, the function arguments are resolved either by their position (positional, or non-keyword arguments) or by their specified name (keyword arguments).

When calling the function you need to put the non-keyword arguments first and lastly the keyword arguments, e.g.

# demonstrate how some additional positional and keyword arguments are passed
f1(1, 2, 3, 4, 5, 6, 7, a4=8, a5=9, a6=10)
# prints:
# 1 2 3 (4, 5, 6, 7) {'a5': 9, 'a4': 8, 'a6': 10}

Now, positional arguments that don't fit into the list of specified arguments in the function definition will be appended to the argument list *args, and keyword arguments that don't fit into the list of specified arguments in the function definition will be inserted into the keyword argument dictionary **kwargs.

like image 30
moooeeeep Avatar answered Oct 17 '22 05:10

moooeeeep


Because passing the key to a keyword argument can be optional if the position of it is unambiguous. Observe:

>>> def f(ui, patch1=None, *patches, **opts):
...     print patch1
... 
>>> f(1, 2)
2
>>> f(1, patch1='a', 3)
  File "<stdin>", line 1
SyntaxError: non-keyword arg after keyword arg
>>> f(1, 'a', 3)
a

As you can see, omitting the key of patch1 render that argument a non-keyword one, thus not triggering the SyntaxError exception.


EDIT: moooeeeep in his answer says that

"patch1 is not a keyword arg, it's a positional arg with a default argument value assigned."

This is not wrong, but the following-case IMO, illustrates why such definition is ambiguous:

>>> def f(ui, p1=None, p2=None, *patches, **opts):
...    print p1, p2
... 
>>> f(1, 'a', 'b', 3)  #p2 is a positional argument with default value?
a b
>>> f(1, p2='b')  #p2 is a keyword argument?
None b

HTH!

like image 2
mac Avatar answered Oct 17 '22 05:10

mac


I believe when calling a function with:

function(arg1="value")

This would be using a "keyword" argument, but when defining a function's interface with:

def function(arg1="value"):

you're defining a "default value". ()

So to answer your question; Having a default value after a positional argument is perfectly normal, and so is calling a function with a non-keyword argument before a keyword.

Also note, when calling a function, you cannot have a non-keyword argument after a keyword.

like image 2
potato Avatar answered Oct 17 '22 05:10

potato