diff -r 7a9acbaa9faa -r 9748190df418 basic_python/intro.rst --- a/basic_python/intro.rst Wed Sep 09 14:39:47 2009 +0530 +++ b/basic_python/intro.rst Tue Sep 15 17:02:07 2009 +0530 @@ -91,7 +91,8 @@ appearance might differ based on the version of Python being used. The ``>>>`` thing shown is the python prompt. When something is typed at the prompt and the enter key is hit, the python interpreter interprets the command entered and -performs the appropriate action. +performs the appropriate action. All the examples presented in this document are +to be tried hands on, on the interactive interpreter. :: @@ -196,6 +197,11 @@ This example is to show that unlike in C or C++ there is no limit on the value of an integer. +Try this on the interactive interpreter: +``import this`` + +*Hint: The output gives an idea of Power of Python* + *ipython* - An enhanced interactive Python interpreter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -277,7 +283,7 @@ print gcd(72, 92) To run the script, open the shell prompt, navigate to the directory that -contains the python file and run `python ` at the prompt ( in this +contains the python file and run ``python `` at the prompt ( in this case filename is gcd.py ) **Running the python script** @@ -289,7 +295,7 @@ Another method to run a python script would be to include the line -`#! /usr/bin/python` +``#! /usr/bin/python`` at the beginning of the python file and then make the file executable by @@ -340,7 +346,7 @@ The only distinction comes during type checking (which is not a healthy practice). Long numbers are tucked with a trailing 'L' just to signify that they are long. Notice that in the example just lng at the prompt displays the value of the variable -with the 'L' whereas `print lng` displays without the 'L'. This is because print +with the 'L' whereas ``print lng`` displays without the 'L'. This is because print formats the output before printing. Also in the example, notice that adding an integer to a long does not give any errors and the result is as expected. So for all practical purposes longs can be treated as ints. @@ -409,3 +415,566 @@ Strings ~~~~~~~ +Strings are one of the essential data structures of any programming language. +The ``print "Hello, World!"`` program was introduced in the earlier section, and +the *"Hello, World!"* in the print statement is a string. A string is basically +a set of characters. Strings can be represented in various ways shown below: + +:: + + s = 'this is a string' # a string variable can be represented using single quotes + s = 'This one has "quotes" inside!' # The string can have quotes inside it as shown + s = "I have 'single-quotes' inside!" + l = "A string spanning many lines\ + one more line\ + yet another" # a string can span more than a single line. + t = """A triple quoted string does # another way of representing multiline strings. + not need to be escaped at the end and + "can have nested quotes" etc.""" + +Try the following on the interpreter: +``s = 'this is a string with 'quotes' of similar kind'`` + +**Exercise: How to use single quotes within single quotes in a string as shown +in the above example without getting an error?** + +String operations +----------------- + +A few basic string operations are presented here. + +**String concatenation** +String concatenation is done by simple addition of two strings. + +:: + + >>> x = 'Hello' + >>> y = ' Python' + >>> print x+y + Hello Python + +*Try this yourself:* + +:: + + >>> somenum = 13 + >>> print x+somenum + +The problem with the above example is that here a string variable and an integer +variable are trying to be concantenated. To obtain the desired result from the +above example the str(), repr() and the `` can be used. + +**str()** simply converts a value to a string in a reasonable form. +**repr()** creates a string that is a representation of the value. + +The difference can be seen in the example shown below: + +:: + + >>> str(1000000000000000000000000000000000000000000000000L) + '1000000000000000000000000000000000000000000000000' + >>> repr(1000000000000000000000000000000000000000000000000L) + '1000000000000000000000000000000000000000000000000L' + +It can be observed that the 'L' in the long value shown was omitted by str(), +whereas repr() converted that into a string too. An alternative way of using +repr(value) is ```value```. + +A few more examples: +:: + + >>> x = "Let's go \nto Pycon" + >>> print x + Let's go + to Pycon + +In the above example, notice that the '\n'(newline) character is formatted and +the string is printed on two lines. The strings discussed until now were normal +strings. Other than these there are two other types of strings namely, raw strings +and unicode strings. + +**Raw strings** are strings which are unformatted, that is the backslashes(\) are +not parsed and are left as it is in the string. Raw strings are represented with +an 'r' at the start of a string. +Let us look at an example + +:: + + >>> x = r"Let's go \nto Pycon" + >>> print x + Let's go \nto Pycon + +Note: The '\n' is not being parsed into a new line and is left as it is. + +*Try this yourself:* + +:: + + >>> x = r"Let's go to Pycon\" + +**Unicode strings** are strings where the characters are Unicode characters as +opposed to ASCII characters. Unicode strings are represented with a 'u' at the +start of the string. +Let us look at an example: + +:: + + >>> x = u"Let's go to Pycon!" + >>> print x + Let's go to Pycon! + +Boolean +~~~~~~~ + +Python also provides special Boolean datatype. A boolean variable can assume a +value of either *True* or *False* (Note the capitalizations). + +Let us look at examples: + +:: + + >>> t = True + >>> f = not t + >>> print f + False + >>> f or t + True + >>> f and t + False + +The **while** loop +~~~~~~~~~~~~~~~~~~ + +The Python **while** loop is similar to the C/C++ while loop. The syntax is as +follows: + +:: + + statement 0 + while condition: + statement 1 #while block + statement 2 #while block + statement 3 #outside the while block. + +Let us look at an example: + +:: + + >>> x = 1 + >>> while x <= 5: + ... print x + ... x += 1 + ... + 1 + 2 + 3 + 4 + 5 + +The **if** conditional +~~~~~~~~~~~~~~~~~~~~~~ + +The Python **if** block provides the conditional execution of statements. +If the condition evaluates as true the block of statements defined under the if +block are executed. + +If the first block is not executed on account of the condition not being satisfied, +the set of statements in the **else** block are executed. + +The **elif** block provides the functionality of evaluation of multiple conditions +as shown in the example. + +The syntax is as follows: + +:: + + if condition : + statement_1 + statement_2 + + elif condition: + statement_3 + statement_4 + else: + statement_5 + statement_6 + +Let us look at an example: + +:: + + >>> n = raw_input("Input a number:") + >>> if n < 0: + print n," is negative" + elif n > 0: + print n," is positive" + else: + print n, " is 0" + +**raw_input()** +~~~~~~~~~~~~~~~ + +In the previous example we saw the call to the raw_input() subroutine. +The **raw_input()** method is used to take user inputs through the console. +Unlike **input()** which assumes the data entered by the user as a standard python +expression, **raw_input()** treats all the input data as raw data and converts +everything into a string. To illustrate this let us look at an example. + +:: + + >>> input("Enter a number thats a palindrome:") + Enter a number thats a palindrome:121 + 121 + + >>> input("Enter your name:") + Enter your name:PythonFreak + Traceback (most recent call last): + File "", line 1, in + File "", line 1, in + NameError: name 'PythonFreak' is not defined + +As shown above the **input()** assumes that the data entered is a valid Python +expression. In the first call it prompts for an integer input and when entered +it accepts the integer as an integer, whereas in the second call, when the string +is entered without the quotes, **input()** assumes that the entered data is a valid +Python expression and hence it raises and exception saying PythonFreak is not +defined. + +:: + + >>> input("Enter your name:") + Enter your name:'PythonFreak' + 'PythonFreak' + >>> + +Here the name is accepted because its entered as a string (within quotes). But +its unreasonable to go on using quotes each time a string is entered. Hence the +alternative is to use **raw_input()**. + +Let us now look at how **raw_input()** operates with an example. + +:: + + >>> raw_input("Enter your name:") + Enter your name:PythonFreak + 'PythonFreak' + +Observe that the **raw_input()** is converting it into a string all by itself. + +:: + + >>> pal = raw_input("Enter a number thats a palindrome:") + Enter a number thats a palindrome:121 + '121' + +Observe that **raw_input()** is converting the integer 121 also to a string as +'121'. Let us look at another example: + +:: + + >>> pal = raw_input("Enter a number thats a palindrome:") + Enter a number thats a palindrome:121 + >>> pal + 2 + Traceback (most recent call last): + File "", line 1, in + TypeError: cannot concatenate 'str' and 'int' objects + >>> pal + '121' + +Observe here that the variable *pal* is a string and hence integer operations +cannot be performed on it. Hence the exception is raised. + +**int()** method +~~~~~~~~~~~~~~~~ + +Generally for computing purposes, the data used is not strings or raw data but +on integers, floats and similar mathematical data structures. The data obtained +from **raw_input()** is raw data in the form of strings. In order to obtain integers +from strings we use the method **int()**. + +Let us look at an example. + +:: + + >>> intpal = int(pal) + >>> intpal + 121 + +In the previous example it was observed that *pal* was a string variable. Here +using the **int()** method the string *pal* was converted to an integer variable. + +*Try This Yourself:* + +:: + + >>> stringvar = raw_input("Enter a name:") + Enter a name:Guido Van Rossum + >>> stringvar + 'Guido Van Rossum' + >>> numvar = int(stringvar) + + +Functions in Python: **def** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +*Functions* allow us to enclose a set of statements and call the function again +and again instead of repeating the group of statements everytime. Functions also +allow us to isolate a piece of code from all the other code and provides the +convenience of not polluting the global variables. + +*Function* in python is defined with the keyword **def** followed by the name +of the function, in turn followed by a pair of parenthesis which encloses the +list of parameters to the function. The definition line ends with a ':'. The +definition line is followed by the body of the function intended by one block. +The *Function* must return a value:: + + def factorial(n): + fact = 1 + for i in range(2, n): + fact *= i + + return fact + +The code snippet above defines a function with the name factorial, takes the +number for which the factorial must be computed, computes the factorial and +returns the value. + +A *Function* once defined can be used or called anywhere else in the program. We +call a fucntion with its name followed by a pair of parenthesis which encloses +the arguments to the function. + +The value that function returns can be assigned to a variable. Let's call the +above function and store the factorial in a variable:: + + fact5 = factorial(5) + +The value of fact5 will now be 120, which is the factorial of 5. Note that we +passed 5 as the argument to the function. + +It may be necessary to document what the function does, for each of the function +to help the person who reads our code to understand it better. In order to do +this Python allows the first line of the function body to be a string. This +string is called as *Documentation String* or *docstring*. *docstrings* prove +to be very handy since there are number of tools which can pull out all the +docstrings from Python functions and generate the documentation automatically +from it. *docstrings* for functions can be written as follows:: + + def factorial(n): + 'Returns the factorial for the number n.' + fact = 1 + for i in range(2, n): + fact *= i + + return fact + +An important point to note at this point is that, a function can return any +Python value or a Python object, which also includes a *Tuple*. A *Tuple* is +just a collection of values and those values themselves can be of any other +valid Python datatypes, including *Lists*, *Tuples*, *Dictionaries* among other +things. So effectively, if a function can return a tuple, it can return any +number of values through a tuple + +Let us write a small function to swap two values:: + + def swap(a, b): + return b, a + + c, d = swap(a, b) + +Function scope +--------------- +The variables used inside the function are confined to the function's scope +and doesn't pollute the variables of the same name outside the scope of the +function. Also the arguments passed to the function are passed by-value if +it is of basic Python data type:: + + def cant_change(n): + n = 10 + + n = 5 + cant_change(n) + +Upon running this code, what do you think would have happened to value of n +which was assigned 5 before the function call? If you have already tried out +that snippet on the interpreter you already know that the value of n is not +changed. This is true of any immutable types of Python like *Numbers*, *Strings* +and *Tuples*. But when you pass mutable objects like *Lists* and *Dictionaries* +the values are manipulated even outside the function:: + + >>> def can_change(n): + ... n[1] = James + ... + + >>> name = ['Mr.', 'Steve', 'Gosling'] + >>> can_change(name) + >>> name + ['Mr.', 'James', 'Gosling'] + +If nothing is returned by the function explicitly, Python takes care to return +None when the funnction is called. + +Default Arguments +----------------- + +There may be situations where we need to allow the functions to take the +arguments optionally. Python allows us to define function this way by providing +a facility called *Default Arguments*. For example, we need to write a function +that returns a list of fibonacci numbers. Since our function cannot generate an +infinite list of fibonacci numbers, we need to specify the number of elements +that the fibonacci sequence must contain. Suppose, additionally, we want to the +function to return 10 numbers in the sequence if no option is specified we can +define the function as follows:: + + def fib(n=10): + fib_list = [0, 1] + for i in range(n - 2): + next = fib_list[-2] + fib_list[-1] + fib_list.append(next) + return fib_list + +When we call this function, we can optionally specify the value for the +parameter n, during the call as an argument. Calling with no argument and +argument with n=5 returns the following fibonacci sequences:: + + fib() + [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + fib(5) + [0, 1, 1, 2, 3] + +Keyword Arguments +----------------- + +When a function takes a large number of arguments, it may be difficult to +remember the order of the parameters in the function definition or it may +be necessary to pass values to only certain parameters since others take +the default value. In either of these cases, Python provides the facility +of passing arguments by specifying the name of the parameter as defined in +the function definition. This is known as *Keyword Arguments*. + +In a function call, *Keyword arguments* can be used for each argument, in the +following fashion:: + + argument_name=argument_value + Also denoted as: keyword=argument + + def wish(name='World', greetings='Hello'): + print "%s, %s!" % (greetings, name) + +This function can be called in one of the following ways. It is important to +note that no restriction is imposed in the order in which *Keyword arguments* +can be specified. Also note, that we have combined *Keyword arguments* with +*Default arguments* in this example, however it is not necessary:: + + wish(name='Guido', greetings='Hey') + wish(greetings='Hey', name='Guido') + +Calling functions by specifying arguments in the order of parameters specified +in the function definition is called as *Positional arguments*, as opposed to +*Keyword arguments*. It is possible to use both *Positional arguments* and +*Keyword arguments* in a single function call. But Python doesn't allow us to +bungle up both of them. The arguments to the function, in the call, must always +start with *Positional arguments* which is in turn followed by *Keyword +arguments*:: + + def my_func(x, y, z, u, v, w): + # initialize variables. + ... + # do some stuff + ... + # return the value + +It is valid to call the above functions in the following ways:: + + my_func(10, 20, 30, u=1.0, v=2.0, w=3.0) + my_func(10, 20, 30, 1.0, 2.0, w=3.0) + my_func(10, 20, z=30, u=1.0, v=2.0, w=3.0) + my_func(x=10, y=20, z=30, u=1.0, v=2.0, w=3.0) + +Following lists some of the invalid calls:: + + my_func(10, 20, z=30, 1.0, 2.0, 3.0) + my_func(x=10, 20, z=30, 1.0, 2.0, 3.0) + my_func(x=10, y=20, z=30, u=1.0, v=2.0, 3.0) + +Parameter Packing and Unpacking +------------------------------- + +The positional arguments passed to a function can be collected in a tuple +parameter and keyword arguments can be collected in a dictionary. Since keyword +arguments must always be the last set of arguments passed to a function, the +keyword dictionary parameter must be the last parameter. The function definition +must include a list explicit parameters, followed by tuple paramter collecting +parameter, whose name is preceded by a *****, for collecting positional +parameters, in turn followed by the dictionary collecting parameter, whose name +is preceded by a ****** :: + + def print_report(title, *args, **name): + """Structure of *args* + (age, email-id) + Structure of *name* + { + 'first': First Name + 'middle': Middle Name + 'last': Last Name + } + """ + + print "Title: %s" % (title) + print "Full name: %(first)s %(middle)s %(last)s" % name + print "Age: %d\nEmail-ID: %s" % args + +The above function can be called as. Note, the order of keyword parameters can +be interchanged:: + + >>> print_report('Employee Report', 29, 'johny@example.com', first='Johny', + last='Charles', middle='Douglas') + Title: Employee Report + Full name: Johny Douglas Charles + Age: 29 + Email-ID: johny@example.com + +The reverse of this can also be achieved by using a very identical syntax while +calling the function. A tuple or a dictionary can be passed as arguments in +place of a list of *Positional arguments* or *Keyword arguments* respectively +using ***** or ****** :: + + def print_report(title, age, email, first, middle, last): + print "Title: %s" % (title) + print "Full name: %s %s %s" % (first, middle, last) + print "Age: %d\nEmail-ID: %s" % (age, email) + + >>> args = (29, 'johny@example.com') + >>> name = { + 'first': 'Johny', + 'middle': 'Charles', + 'last': 'Douglas' + } + >>> print_report('Employee Report', *args, **name) + Title: Employee Report + Full name: Johny Charles Douglas + Age: 29 + Email-ID: johny@example.com + +Nested Functions and Scopes +--------------------------- + +Python allows nesting one function inside another. This style of programming +turns out to be extremely flexible and powerful features when we use *Python +decorators*. We will not talk about decorators is beyond the scope of this +course. If you are interested in knowing more about *decorator programming* in +Python you are suggested to read: + +| http://avinashv.net/2008/04/python-decorators-syntactic-sugar/ +| http://personalpages.tds.net/~kent37/kk/00001.html + +However, the following is an example for nested functions in Python:: + + def outer(): + print "Outer..." + def inner(): + print "Inner..." + print "Outer..." + inner() + + >>> outer() +