Browse DevX
Sign up for e-mail newsletters from DevX


A Developer's Guide to Python 3.0: Package Installation and More Language Enhancements : Page 3

Explore Python 3.0's new support for per-user installations, an official with statement, property decorators, keyword-only arguments, dictionary changes, and C API changes.




Building the Right Environment to Support AI, Machine Learning and Deep Learning

PEP-3102: Keyword-Only Arguments

Python always supported keyword arguments. But, it wasn't possible to have keyword-only arguments. Given the following function:

def f(a, b, c='default'): pass

You must provide the a and b arguments when calling f, either as positional arguments (a is first and b is second), or using keyword arguments:

# Calling f with a and b as positional arguments f(3, 4) # Calling f with a and b as keyword arguments f(a=3, b=4) # Order doesn't matter when using keyword arguments f(b=4, a=3)

The c argument is optional because it has a default value. You can provide the c argument either as a positional argument or as a keyword argument:

f(3, 4, 5) f(3, 4, c=5) f(3, b=4, c=5)

So far, so good. Python also supports functions with a variable number of arguments via a vararg argument, which is simply a list:

def f(*args): print(sum(args)) f(1, 2, 3) Output: 6

The problem is that up to Python 2.5 it wasn't possible to specify named keyword arguments after the vararg argument:

# This is illegal def f(a, b, *args, d=4): pass

You could have a dictionary of keyword arguments after the vararg, but the keyword arguments would have to be extracted from the dictionary and will have no default value.

def f(*args, **kw): pass

Python 3.0 provides a new syntax to support keyword-only arguments following vararg in the function signature. There are two cases:

  1. You have zero or more fully-specified positional arguments preceding a vararg variable that's followed by keyword-only arguments.
  2. >>> def f(a, b, *c, d=5): ... print(a, b, c, d) ... >>> f(1,2,3) 1 2 (3,) 5 >>> f(1,2,3,4,5) 1 2 (3, 4, 5) 5 >>> f(1,2,d=4) 1 2 () 4 >>> f(1,d=4) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f() takes at least 2 non-keyword positional arguments (1 given) >>> f(1,d=4, b=2) 1 2 () 4

    As you can see, a and b must be passed to f() either by position or by name. There may be additional positional arguments after a and b. Finally, d can be passed by name only (otherwise it will be considered a vararg). If you don't pass both a and b you will get a TypeError.

  3. You have zero or more fully-specified positional arguments followed immediately by keyword-only arguments (no varargs):
  4. >>> def f(a, b, *, d): ... print (a, b, d) ... >>> f(1,2,3) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: f() takes exactly 2 positional arguments (3 given) >>> f(1,2,d=3) 1 2 3 >>> f(a=1,2, d=3) File "<stdin>", line 1 SyntaxError: non-keyword arg after keyword arg >>> f(1, d=3, b=2) 1 2 3

The lone asterisk marks the end of the positional arguments (a and b) and the start of the keyword-only arguments (d).

Another interesting observation is that both positional arguments and keyword-only argument may or may not have a default value. The only caveat is that for both positional arguments and keyword-only arguments you may not have a non-default argument follow an argument with a default. Here's a function where the positional argument has a default and the keyword argument doesn't:

>>> def f(x=4, *, y): ... print(x, y) ... >>> f(y=3) 4 3 >>> f(y=2, 6) File "<stdin>", line 1 SyntaxError: non-keyword arg after keyword arg >>> f(6, y=5) 6 5 >>> f(y=5, x=7) 7 5

This function requires that you specify y by name. You may omit x altogether because it has a default value (4). If you do pass x as a positional argument it must come before the y keyword-only argument. If you pass x by name too, then you can pass it after the y argument.

In all cases you may also have the catchall **keywords dictionary of optional keyword arguments. Here is the ultimate function that has positional arguments (with and without a default), varargs, keyword-only argument and a keywords dict:

>>> def f(a, b=2, *c, d, **e): ... print (a, b, c, d, e) ... >>> f(1,2,3,d=4,e=5) 1 2 (3,) 4 {'e': 5} >>> f(1,2,3,e=5, d=4) 1 2 (3,) 4 {'e': 5}

PEP-3104: Access to Names in Outer Scope

Python supports nested scopes via nested functions. Names that are bound in a function scope are also visible in nested functions and classes.

def outer(n): def inner(): print n inner() external(3) Output: 3

In Python 2.x, nested functions and classes couldn't modify outer scope variables because trying to do so would simply create a new local variable in the nested function, without modifying the outer variable:

def outer(n): def inner(): n = 5 print 'inner: n=', n inner() print 'outer: n=', n outer(3) Output: inner: n= 5 outer: n= 3

Python did have global scope variables that could be modified inside functions and even nested functions:

n = 0 def outer(): def inner(): global n n = 7 inner() print n outer() Output: 7

In contrast, Python 3.0 allows nested functions and classes to modify non-global outer names by using the new keyword nonlocal. Its usage is similar to global:

def outer(): n = 6 def inner(): nonlocal n n = 9 inner() print(n) outer()

Note, that the code first declares nonlocal n and then in the next line assigns it a value. The PEP claims that you can do it in one line but the interpreter disagrees:

nonlocal n = 9 ^ SyntaxError: invalid syntax

This behavior matches the global keyword. It would be nice if both nonlocal and global could be used with the shorthand one-liner.

Here's a more comprehensive example of nonlocal used in a class defined inside a function (yes, that's allowed). The outer function defines a class named "A" internally, containing a nested function called makeA() that creates an instance of A. The outer function also accepts an argument n, which is in its scope of course. The A.__init__ method increments this number by one and prints the resulting value. Finally, the outer function returns the makeA function as its return value. The reason for this complicated arrangement is to show how name binding works.

After defining the outer function has been defined, the code calls it twice, with n=3 and n=7, and assigns the results to the variables make_1 and make_a2. Remember, the return value of outer is the nested function makeA(), so make_a1 and make_a2 are functions that create an instance of the inner class A (which is not visible from outside the outer function, and can't be created directly). The interesting thing is that make_a1 and make_a2 are not identical. They both create an instance of the same class A, but the nonlocal binding is different. This is demonstrated below by calling make_a1() three times in a row:

def outer(n): class A(object): def __init__(self): nonlocal n n += 1 print(n) def makeA(): return A() return makeA make_a1 = outer(3) make_a2 = outer(7) make_a1() make_a1() make_a1() print('---') make_a2() make_a2() Output: 4 5 6 --- 8 9

As the preceding output shows, the three calls to make_a1 produces the results 4, 5, and 6 (initially n=3 and it gets incremented three times) and then calling make_a2() twice, resulting in 8 and 9 (initially n=7 and it gets incremented twice).

Thanks for your registration, follow us on our social networks to keep up-to-date