To define a function in a program is very simple:
>>> def function_name(parameter list): ... instruction1 ... instruction2 ... etc.
The offset, as always, is mandatory, and the convention is to use function names in lower_case: no capital letters, and _ if necessary.
The keyword return can be used to specify the return of the function:
>>> def increment(s): ... y = x + 1 ... return there ... >>> incremental(5) 6
If no return is specified, then the return will be "None".
A function can be called recursively. Let's illustrate this with a factorial function....
>>> def facto(n): ... if n == 1 : ... return 1 ... else : ... return n*facto(n-1) ... >>> facto(10)
The following formula gives the relationship between a temperature expressed in degrees Celsius $c$ and the same temperature expressed in degrees Fahrenheit $f$.
$f = 32 + 1.8 \times c $
Give the code of a python program that
For a function, there are special parameters capable of intercepting an indefinite number of values. The signature of the function becomes polymorphic.
>>> def toto(x, *args): ... print args
>>> toto(5, 2,'a') (2, 'a')
>>> toto(5,'a', r = 2)
File "<stdin>", line 1, in <module> TypeError: toto() got an unexpected keyword argument 'r' argument >>> >>> def toto(x, *args, **kw): ... print args ... print kw ... >>> >>> toto(5, 2,'a') (2, 'a') {} >>> toto(5,'a', r = 2) ('a',) {'r': 2}
These special parameters should be used sparingly.
The generators, introduced in version 2.2, offer an original programming technique, which allows a function or method to return intermediate results.
The keyword return is replaced by yield.
The first time this word is encountered, the execution is stopped and a generator object is returned, which contains the local context of the function, and a next method.
Each time this method is called, the function is called until the next yield.
We define a function with three yield (and no return). We'll see that
Let's define our generator:
>>> def ca_count(): ... yield "And one yield" ... yield "And two" ... yield "And three." ... >>> I_count = ca_count()
First call....
>>> I_count.next() "And one yield"
And it goes on like that, from yield to yield...
>>> I_count.next() "And two" >>> I_count.next() "And three."
Finally, once all yield have been called, an exception StopIteration is raised...
>>> I_count.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
The next method to move to the next value, and the raising of the exception ''StopIteration', make generators compatible with loops:
>>> I_account = ca_account() >>> for element in I_account: ... print element ... And of a And two of them And three of them
Here is what generators can be used for, in this case to properly program the Fibonacci suite:
>>> def fibonacci(): ... a, b = 0, 1 ... while True: ... yield b ... a, b = b, a+b ... >>> continued = fibonacci() >>> n = input('How many terms? ') >>> for k in range(n): ... print continued.next(), ... 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
We have put a yield in an infinite loop: we can call as many times as we want continued.next(). This approach can be reused in many different contexts.
The following generator allows you to obtain as many prime numbers as you want:
>>> def number_first(): ... number = 2 ... first =[2] ... yield number ... while True: ... number += 1 ... if len([k for k in first if number%k == 0]) == 0: ... yield number ... first.appendix(number) ... >>> first = first_number() >>> first.next() 2 >>> first.next() 3 >>> first.next() 5 >>> first.next() 7 >>> first.next() 11
Generators expressions (genexp) provide a concise form for simple generators by using list comprehensions: the brackets of these last are replaced by parentheses...
>>> gen = ( k for k in range(10) if k%3 == 0) >>> gen.next() 0 >>> gen.next() 3 >>> gen.next() 6 >>> gen.next() 9 >>> gen.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration
The module itertools offers advanced tools to work on generators.
Python being an interpreted language, it does not know in advance the code it will execute: unlike other languages, there is no optimization of frequently used function calls, by a call to a single memory address.
To avoid the problem, and save time each time a function is called again, it can be stored in a local variable, as illustrated in the following code:
>>> from time import time >>> def toto(N): ... x=float(N)/1000 ... x=1000*xx ... >>> def without(N): ... dd=time() ... for k in range(N): ... toto(k) ... return time()-dd ... >>> def with(N): ... tt=toto ... dd=time() ... for k in range(N): ... tt(k) ... return time()-dd ...
In the above, the function without calls N times the function toto, without having stopped it in a variable beforehand, when the function with does the same thing, using a local variable for toto. Let's look at the number of times "with" is faster than "without"...
>>> cpt = 0 >>> for k in range(1000): ... if without(10000) > with(10000): ... cpt+=1 ... >>> print float(cpt)/10 93.2
This saves time in 93.2% of cases.