Defining Functions
We can create a function that writes the Fibonacci series to an arbitrary boundary:
>> def fib(n): # write Fibonacci series up to n
... """Print a Fibonacci series up to n.""" ... a, b = 0, 1 ... while a < n: ... print(a, end=' ') ... a, b = b, a+b ... print() ... >>> # Now call the function we just defined: ... fib(2000) 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
The keyword
The first statement of the function body can optionally be a string literal;
this string literal is the function’s documentation string, or docstring.
There are tools which use docstrings to automatically produce online or printed
documentation, or to let the user interactively browse through code; it’s good
practice to include docstrings in code that you write, so make a habit of it.def
introduces a function definition. It must be
followed by the function name and the parenthesized list of formal parameters.
The statements that form the body of the function start at the next line, and
must be indented.The execution of a function introduces a new symbol table used for the local variables of the function. More precisely, all variable assignments in a function store the value in the local symbol table; whereas variable references first look in the local symbol table, then in the local symbol tables of enclosing functions, then in the global symbol table, and finally in the table of built-in names. Thus, global variables cannot be directly assigned a value within a function (unless named in a
global
statement), although they
may be referenced.The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object). [1] When a function calls another function, a new local symbol table is created for that call.
A function definition introduces the function name in the current symbol table. The value of the function name has a type that is recognized by the interpreter as a user-defined function. This value can be assigned to another name which can then also be used as a function. This serves as a general renaming mechanism:
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
fib
is not a function but
a procedure since it doesn’t return a value. In fact, even functions without a
return
statement do return a value, albeit a rather boring one. This
value is called None
(it’s a built-in name). Writing the value None
is
normally suppressed by the interpreter if it would be the only value written.
You can see it if you really want to using print()
:>>> fib(0)
>>> print(fib(0))
None
>>> def fib2(n): # return Fibonacci series up to n
... """Return a list containing the Fibonacci series up to n."""
... result = []
... a, b = 0, 1
... while a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
- The
return
statement returns with a value from a function.return
without an expression argument returnsNone
. Falling off the end of a function also returnsNone
. - The statement
result.append(a)
calls a method of the list objectresult
. A method is a function that ‘belongs’ to an object and is namedobj.methodname
, whereobj
is some object (this may be an expression), andmethodname
is the name of a method that is defined by the object’s type. Different types define different methods. Methods of different types may have the same name without causing ambiguity. (It is possible to define your own object types and methods, using classes, see Classes) The methodappend()
shown in the example is defined for list objects; it adds a new element at the end of the list. In this example it is equivalent toresult = result + [a]
, but more efficient.
4.7. More on Defining Functions
It is also possible to define functions with a variable number of arguments. There are three forms, which can be combined.4.7.1. Default Argument Values
The most useful form is to specify a default value for one or more arguments. This creates a function that can be called with fewer arguments than it is defined to allow. For example:def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries < 0:
raise OSError('uncooperative user')
print(complaint)
- giving only the mandatory argument:
ask_ok('Do you really want to quit?')
- giving one of the optional arguments:
ask_ok('OK to overwrite the file?', 2)
- or even giving all arguments:
ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!')
in
keyword. This tests whether or
not a sequence contains a certain value.The default values are evaluated at the point of function definition in the defining scope, so that
i = 5
def f(arg=i):
print(arg)
i = 6
f()
5
.Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. For example, the following function accumulates the arguments passed to it on subsequent calls:
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
[1]
[1, 2]
[1, 2, 3]
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
4.7.2. Keyword Arguments
Functions can also be called using keyword arguments of the formkwarg=value
. For instance, the following function:def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
voltage
) and three optional arguments
(state
, action
, and type
). This function can be called in any
of the following ways:parrot(1000) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
parrot() # required argument missing
parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
parrot(110, voltage=220) # duplicate value for the same argument
parrot(actor='John Cleese') # unknown keyword argument
actor
is not a valid argument for the
parrot
function), and their order is not important. This also includes
non-optional arguments (e.g. parrot(voltage=1000)
is valid too).
No argument may receive a value more than once.
Here’s an example that fails due to this restriction:>>> def function(a):
... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'
**name
is present, it receives a
dictionary (see Mapping Types — dict) containing all keyword arguments except for
those corresponding to a formal parameter. This may be combined with a formal
parameter of the form *name
(described in the next subsection) which
receives a tuple containing the positional arguments beyond the formal parameter
list. (*name
must occur before **name
.) For example, if we define a
function like this:def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
keys = sorted(keywords.keys())
for kw in keys:
print(kw, ":", keywords[kw])
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
keys()
method before printing its contents;
if this is not done, the order in which the arguments are printed is undefined.4.7.3. Arbitrary Argument Lists
Finally, the least frequently used option is to specify that a function can be
called with an arbitrary number of arguments. These arguments will be wrapped
up in a tuple (see Tuples and Sequences). Before the variable number of arguments,
zero or more normal arguments may occur.
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
variadic
arguments will be last in the list of formal
parameters, because they scoop up all remaining input arguments that are
passed to the function. Any formal parameters which occur after the *args
parameter are ‘keyword-only’ arguments, meaning that they can only be used as
keywords rather than positional arguments.>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
4.7.4. Unpacking Argument Lists
The reverse situation occurs when the arguments are already in a list or tuple but need to be unpacked for a function call requiring separate positional arguments. For instance, the built-inrange()
function expects separate
start and stop arguments. If they are not available separately, write the
function call with the *
-operator to unpack the arguments out of a list
or tuple:>>> list(range(3, 6)) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked from a list
[3, 4, 5]
In the same fashion, dictionaries can deliver keyword arguments with the
**
-operator:>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
4.7.5. Lambda Expressions
Small anonymous functions can be created with thelambda
keyword.
This function returns the sum of its two arguments: lambda a, b: a+b
.
Lambda functions can be used wherever function objects are required. They are
syntactically restricted to a single expression. Semantically, they are just
syntactic sugar for a normal function definition. Like nested function
definitions, lambda functions can reference variables from the containing
scope:>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
Errors and Exceptions
Until now error messages haven’t been more than mentioned, but if you have tried out the examples you have probably seen some. There are (at least) two distinguishable kinds of errors: syntax errors and exceptions.8.1. Syntax Errors
Syntax errors, also known as parsing errors, are perhaps the most common kind of complaint you get while you are still learning Python:>>> while True print('Hello world')
File "<stdin>", line 1, in ?
while True print('Hello world')
^
SyntaxError: invalid syntax
print()
, since a colon
(':'
) is missing before it. File name and line number are printed so you
know where to look in case the input came from a script.8.2. Exceptions
Even if a statement or expression is syntactically correct, it may cause an error when an attempt is made to execute it. Errors detected during execution are called exceptions and are not unconditionally fatal: you will soon learn how to handle them in Python programs. Most exceptions are not handled by programs, however, and result in error messages as shown here:>>> 10 * (1/0)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
ZeroDivisionError: division by zero
>>> 4 + spam*3
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: Can't convert 'int' object to str implicitly
ZeroDivisionError
, NameError
and TypeError
.
The string printed as the exception type is the name of the built-in exception
that occurred. This is true for all built-in exceptions, but need not be true
for user-defined exceptions (although it is a useful convention). Standard
exception names are built-in identifiers (not reserved keywords).The rest of the line provides detail based on the type of exception and what caused it.
The preceding part of the error message shows the context where the exception happened, in the form of a stack traceback. In general it contains a stack traceback listing source lines; however, it will not display lines read from standard input.
Built-in Exceptions lists the built-in exceptions and their meanings.
8.3. Handling Exceptions
It is possible to write programs that handle selected exceptions. Look at the following example, which asks the user for input until a valid integer has been entered, but allows the user to interrupt the program (usingControl-C
or
whatever the operating system supports); note that a user-generated interruption
is signalled by raising the KeyboardInterrupt
exception.>>> while True:
... try:
... x = int(input("Please enter a number: "))
... break
... except ValueError:
... print("Oops! That was no valid number. Try again...")
...
try
statement works as follows.- First, the try clause (the statement(s) between the
try
andexcept
keywords) is executed. - If no exception occurs, the except clause is skipped and execution of the
try
statement is finished. - If an exception occurs during execution of the try clause, the rest of the
clause is skipped. Then if its type matches the exception named after the
except
keyword, the except clause is executed, and then execution continues after thetry
statement. - If an exception occurs which does not match the exception named in the except
clause, it is passed on to outer
try
statements; if no handler is found, it is an unhandled exception and execution stops with a message as shown above.
try
statement may have more than one except clause, to specify
handlers for different exceptions. At most one handler will be executed.
Handlers only handle exceptions that occur in the corresponding try clause, not
in other handlers of the same try
statement. An except clause may
name multiple exceptions as a parenthesized tuple, for example:... except (RuntimeError, TypeError, NameError):
... pass
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("OS error: {0}".format(err))
except ValueError:
print("Could not convert data to an integer.")
except:
print("Unexpected error:", sys.exc_info()[0])
raise
try
... except
statement has an optional else
clause, which, when present, must follow all except clauses. It is useful for
code that must be executed if the try clause does not raise an exception. For
example:for arg in sys.argv[1:]:
try:
f = open(arg, 'r')
except IOError:
print('cannot open', arg)
else:
print(arg, 'has', len(f.readlines()), 'lines')
f.close()
else
clause is better than adding additional code to
the try
clause because it avoids accidentally catching an exception
that wasn’t raised by the code being protected by the try
...
except
statement.When an exception occurs, it may have an associated value, also known as the exception’s argument. The presence and type of the argument depend on the exception type.
The except clause may specify a variable after the exception name. The variable is bound to an exception instance with the arguments stored in
instance.args
. For convenience, the exception instance defines
__str__()
so the arguments can be printed directly without having to
reference .args
. One may also instantiate an exception first before
raising it and add any attributes to it as desired.>>> try:
... raise Exception('spam', 'eggs')
... except Exception as inst:
... print(type(inst)) # the exception instance
... print(inst.args) # arguments stored in .args
... print(inst) # __str__ allows args to be printed directly,
... # but may be overridden in exception subclasses
... x, y = inst.args # unpack args
... print('x =', x)
... print('y =', y)
...
<class 'Exception'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs
Exception handlers don’t just handle exceptions if they occur immediately in the try clause, but also if they occur inside functions that are called (even indirectly) in the try clause. For example:
>>> def this_fails():
... x = 1/0
...
>>> try:
... this_fails()
... except ZeroDivisionError as err:
... print('Handling run-time error:', err)
...
Handling run-time error: int division or modulo by zero
8.4. Raising Exceptions
Theraise
statement allows the programmer to force a specified
exception to occur. For example:>>> raise NameError('HiThere')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
NameError: HiThere
raise
indicates the exception to be raised.
This must be either an exception instance or an exception class (a class that
derives from Exception
).If you need to determine whether an exception was raised but don’t intend to handle it, a simpler form of the
raise
statement allows you to
re-raise the exception:>>> try:
... raise NameError('HiThere')
... except NameError:
... print('An exception flew by!')
... raise
...
An exception flew by!
Traceback (most recent call last):
File "<stdin>", line 2, in ?
NameError: HiThere
8.5. User-defined Exceptions
Programs may name their own exceptions by creating a new exception class (see Classes for more about Python classes). Exceptions should typically be derived from theException
class, either directly or indirectly. For
example:>>> class MyError(Exception):
... def __init__(self, value):
... self.value = value
... def __str__(self):
... return repr(self.value)
...
>>> try:
... raise MyError(2*2)
... except MyError as e:
... print('My exception occurred, value:', e.value)
...
My exception occurred, value: 4
>>> raise MyError('oops!')
Traceback (most recent call last):
File "<stdin>", line 1, in ?
__main__.MyError: 'oops!'
__init__()
of Exception
has been
overridden. The new behavior simply creates the value attribute. This
replaces the default behavior of creating the args attribute.Exception classes can be defined which do anything any other class can do, but are usually kept simple, often only offering a number of attributes that allow information about the error to be extracted by handlers for the exception. When creating a module that can raise several distinct errors, a common practice is to create a base class for exceptions defined by that module, and subclass that to create specific exception classes for different error conditions:
class Error(Exception):
"""Base class for exceptions in this module."""
pass
class InputError(Error):
"""Exception raised for errors in the input.
Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""
def __init__(self, expression, message):
self.expression = expression
self.message = message
class TransitionError(Error):
"""Raised when an operation attempts a state transition that's not
allowed.
Attributes:
previous -- state at beginning of transition
next -- attempted new state
message -- explanation of why the specific transition is not allowed
"""
def __init__(self, previous, next, message):
self.previous = previous
self.next = next
self.message = message
Many standard modules define their own exceptions to report errors that may occur in functions they define. More information on classes is presented in chapter Classes.
8.6. Defining Clean-up Actions
Thetry
statement has another optional clause which is intended to
define clean-up actions that must be executed under all circumstances. For
example:>>> try:
... raise KeyboardInterrupt
... finally:
... print('Goodbye, world!')
...
Goodbye, world!
KeyboardInterrupt
Traceback (most recent call last):
File "<stdin>", line 2, in ?
try
statement, whether an exception has occurred or not. When an exception has
occurred in the try
clause and has not been handled by an
except
clause (or it has occurred in an except
or
else
clause), it is re-raised after the finally
clause has
been executed. The finally
clause is also executed “on the way out”
when any other clause of the try
statement is left via a
break
, continue
or return
statement. A more
complicated example:>>> def divide(x, y):
... try:
... result = x / y
... except ZeroDivisionError:
... print("division by zero!")
... else:
... print("result is", result)
... finally:
... print("executing finally clause")
...
>>> divide(2, 1)
result is 2.0
executing finally clause
>>> divide(2, 0)
division by zero!
executing finally clause
>>> divide("2", "1")
executing finally clause
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'
finally
clause is executed in any event. The
TypeError
raised by dividing two strings is not handled by the
except
clause and therefore re-raised after the finally
clause has been executed.In real world applications, the
finally
clause is useful for
releasing external resources (such as files or network connections), regardless
of whether the use of the resource was successful.8.7. Predefined Clean-up Actions
Some objects define standard clean-up actions to be undertaken when the object is no longer needed, regardless of whether or not the operation using the object succeeded or failed. Look at the following example, which tries to open a file and print its contents to the screen.for line in open("myfile.txt"):
print(line, end="")
with
statement allows objects like files to be
used in a way that ensures they are always cleaned up promptly and correctly.with open("myfile.txt") as f:
for line in f:
print(line, end="")
Handling Exceptions
The simplest way to handle exceptions is with a "try-except" block:
If you wanted to examine the exception from code, you could have:
General Error Catching
Sometimes, you want to catch all errors that could possibly be generated, but usually you don't.In most cases, you want to be as specific as possible (CatchWhatYouCanHandle). In the first example above, if you were using a catch-all exception clause and a user presses Ctrl-C, generating a KeyboardInterrupt, you don't want the program to print "divide by zero".
However, there are some situations where it's best to catch all errors.
For
example, suppose you are writing an extension module to a web service.
You want the error information to output the output web page, and the
server to continue to run, if at all possible. But you have no idea what
kind of errors you might have put in your code.
In situations like these, you may want to code something like this:
MoinMoin software is a good example of where general error catching is good. If you write MoinMoin extension macros, and trigger an error, MoinMoin
will give you a detailed report of your error and the chain of events
leading up to it. Python software needs to be able to catch all errors, and deliver them to the recipient of the web page.
Another case is when you want to do something when code fails:
By using raise
with no arguments, you will re-raise the last exception. A common
place to use this would be to roll back a transaction, or undo
operations. If it's a matter of cleanup that should be run regardless
of success or failure, then you would do:
try:
do_some_stuff()
finally:
cleanup_stuff()
No comments:
Post a Comment