.. highlight:: rst First introduction to Python ============================ Fast-track Python ----------------- The python source code is run by an ``interpreter`` either interactively or as script. The common python interpreter can be launched by the ``python`` command from any shell window. In general we recommend not to use the bare python interpreter but an enhanced and much more user friendly version called *IPython*. Start an interactive IPython session by running the ``ipython`` command: .. sourcecode:: ipython ifmp14@ifmp14:~$ ipython Python 2.7.6 (default, Mar 22 2014, 22:59:38) Type "copyright", "credits" or "license" for more information. IPython 2.2.0 -- An enhanced Interactive Python. ? -> Introduction and overview of IPython's features. %quickref -> Quick reference. help -> Python's own help system. object? -> Details about 'object', use 'object??' for extra details. In [1]: .. highlight:: python :linenothreshold: 5 In Python everything is an object and the types are dynamic .. sourcecode:: ipython In [1]: a = 1.5 # The rest of this line is a comment In [2]: type(a) Out[2]: float In [3]: a = 1 # redefine a as an integer In [4]: type(a) Out[4]: int In [5]: a = 1e-10 # redefine a as a float with scientific notation In [6]: type(a) Out[6]: float In [7]: a = 1+5j # redefine a as complex In [8]: type(a) Out[8]: complex In [9]: print("a="+str(a)) a=(1+5j) In [10]: print(a) (1+5j) In [11]: a Out[11]: (1+5j) In [12]: print(2**6) 64 In [13]: a**8 Out[13]: (-3824-456960j) In [14]: 2**6 Out[14]: 64 str, unicode - string types (immutable): .. sourcecode:: ipython In [15]: s1 = 'hello' In [16]: s2 = u'φ(α) + ψ(α+β+γ)' # prepend u, gives unicode string In [17]: s1[0] Out[17]: 'h' In [18]: s1[0], s1[1] Out[18]: ('h', 'e') In [19]: type(s1) Out[19]: str In [20]: type(s2) Out[20]: unicode In [21]: print(s2) # The 'print' shows the string using the actual special characters φ(α) + ψ(α+β+γ) In [22]: s2 # This way we just see the unicode encoding Out[22]: u'\u03c6(\u03b1) + \u03c8(\u03b1+\u03b2+\u03b3)' list - mutable sequence: .. sourcecode:: ipython In [23]: ell = [1,2,'three'] # make list In [24]: type(ell) Out[24]: list In [25]: type(ell[0]) Out[25]: int In [26]: type(ell[2]) Out[26]: str In [27]: ell[2] = 3 In [38]: ell.append(4) In [29]: ell Out[29]: [1, 2, 3, 4] tuple - immutable sequence: .. sourcecode:: ipython In [30]: tempty = () In [31]: toneelement = (1,) # Note the trailing comma here (and only here) In [32]: ttwoelement = (1, 2) In [33]: tempty Out[33]: () In [34]: toneelement Out[34]: (1,) In [35]: ttwoelement Out[35]: (1, 2) In [36]: t = (1, 2, 'four') In [37]: t[2] Out[37]: 'four' In [38]: t[2] = 4 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in () ----> 1 t[2] = 4 TypeError: 'tuple' object does not support item assignment python - loop: .. sourcecode:: ipython In [39]: for element in xrange(1,21,2): ....: print(element) ....: 1 3 5 7 9 11 13 15 17 19 In [40]: for element in xrange(1,21,2): ....: print(element) ....: File "", line 2 print(element) ^ IndentationError: expected an indented block hence indentation is the delimiter for blocks. Usually IPython takes care for you. Pressing Enter is enough. python - functions: A pythonic way of adding first n numbers: .. sourcecode:: python def sf(n): k = 0 result = 0 while True: print("current number to add =" + str(k)) result += k k += 1 if k > n: break return result In the above example note that the indentation delimits the blocks and that no semi-colons are needed. We can enter this in our interactive ipython session .. sourcecode:: ipython In [50]: def sf(n): ....: k = 0 ....: result = 0 ....: while True: ....: print("current number to add = " + str(k)) ....: result += k ....: k += 1 ....: if k > n: ....: break ....: return result ....: In [51]: sf(10) current number to add = 0 current number to add = 1 current number to add = 2 current number to add = 3 current number to add = 4 current number to add = 5 current number to add = 6 current number to add = 7 current number to add = 8 current number to add = 9 current number to add = 10 Out[51]: 55 Note how nicely ipython handles indentation for us when we start new nested blocks. On the other hand we can not make empty lines in this interactive mode because this would be interpreted as end of the input. (Actually, we `can` make empty lines but only directly after opening a new nested block.) For larger pieces of code there is much advantage in copying the above definition of the function ``sf`` into a file, let us call it ``sumy.py``. Python source code files have the extention ``.py``. Then we start ipython in the same directory, and type: .. sourcecode:: ipython In [1]: from sumy import * to load the code. Then we can call the function like: .. sourcecode:: ipython In [2]: sf(3) current number to add = 0 current number to add = 1 current number to add = 2 current number to add = 3 Out[2]: 6 Mutable and immutable objects ----------------------------- .. warning:: Arguments are always passed by assignement. Python's pass-by-assignment scheme isn't quite the same as C++'s reference parameters option, but it turns out to be very similar to the C language's argument-passing model in practice: * Immutable arguments are effectively passed "by value." Objects such as integers and strings are passed by object reference instead of by copying, but because you can't change immutable objects in-place anyhow, the effect is much like making a copy. * Mutable arguments are effectively passed "by pointer." Objects such as lists and dictionaries are also passed by object reference, which is similar to the way C passes arrays as pointers -- mutable objects can be changed in-place in the function, much like C arrays. Of course, if you've never used C, Python's argument-passing mode will seem simpler still -- it involves just the assignment of objects to names, and it works the same whether the objects are mutable or not. .. sourcecode:: ipython In [1]: def f(a): # a is assigned to (references) the passed object ...: a = 99 # changes local variable a only: here simply resets 'a' to a completely different object ...: In [2]: b = 88 In [3]: f(b) # 'a' and 'b' both reference same 88 initially In [4]: print(b) # b not changed 88 Assignment to an argument name inside a function (e.g., ``a=99``) does not magically change a variable like ``b`` in the scope of the function call. Argument names may share passed objects initially (they are essentially pointers to those objects), but only temporarily, when the function is first called. As soon as an argument name is reassigned, this relationship ends. This is also related to the `copy-on-write` semantics. That is the case for assignment to argument names themselves. When arguments are passed mutable objects like lists and dictionaries, we also need to be aware that inplace changes to such objects may live on after a function exits, and hence impact callers. Here's an example that demonstrates this behavior: .. sourcecode:: ipython In [5]: def changer(a,b): # Arguments assigned references to objects ...: a = 2 # Changes local name's value only ...: b[0] = 'spam' # Changes shared object in-place ...: In [6]: X = 1 In [7]: L = [1, 2] In [8]: changer(X,L) # Pass immutable and mutable objects In [9]: X # X is unchanged, L is different! Out[9]: 1 In [10]: L Out[10]: ['spam', 2] If we don't want in-place changes within functions to impact objects we pass to them, though, we can simply make explicit copies of mutable objects. For function arguments, we can always copy the list at the point of call: .. sourcecode:: ipython In [11]: L = [1, 2] In [12]: changer(X, L[:]) # Pass a copy, so our 'L' does not change In [13]: X Out[13]: 1 In [14]: L Out[14]: [1, 2] We can also copy within the function itself, if we never want to change passed-in objects, regardless of how the function is called: .. sourcecode:: ipython In [15]: def changer(a, b): ....: b = b[:] # Copy input list so we don't impact caller ....: a = 2 ....: b[0] = 'spam' # Changes our list copy only ....: In [16]: L = [1, 2] In [17]: changer(X, L) In [18]: X Out[18]: 1 In [19]: L Out[19]: [1, 2] Both of these copying schemes don't stop the function from changing the object -- they just prevent those changes from impacting the caller. To really prevent changes, we can always convert to immutable objects to force the issue. Tuples, for example, throw an exception when changes are attempted: .. sourcecode:: ipython In [20]: changer(X, tuple(L)) # Pass a tuple, so changes are errors --------------------------------------------------------------------------- TypeError Traceback (most recent call last) in () ----> 1 changer(X, tuple(L)) # Pass a tuple, so changes are errors in changer(a, b) 2 b = b[:] 3 a = 2 ----> 4 b[0] = 'spam' # Changes our list copy only 5 TypeError: 'tuple' object does not support item assignment Elements of writing good Python **style** are on `Google Python Style Guide `_ or `PythonStyle `_. Number formatting ----------------- You generally do not want to display a floating point result of a calculation in its raw form, often with an enormous number of digits after the decimal point, like 23.457413902458498. You are likely to prefer rounding it to something like 23.46. Python features sprintf()-style formatting, which you might know already from Matlab or C. The most common format specifiers are ``%d`` for integers, ``%f`` for floating point numbers, ``%e`` for scientific notation and ``%s`` for strings. Examples ^^^^^^^^ A float rounded to 2 digits after the comma with 6 digits in total: .. sourcecode:: ipython In [1]: '%+6.2f' % 0.2 Out[1]: ' +0.20' Scientific notation with 2 digits after the comma: .. sourcecode:: ipython In [2]: s = '%.2e' % 0.2 Out[2]: 2.00e-01 Integer with a fixed size of 5 digits and left-padded with zeros: .. sourcecode:: ipython In [3]: s = '%05d' % 34 print(s) Out[3]: 00034 It is also possible to replace more than one number in a string: .. sourcecode:: ipython In [4]: 'GMRES solver converged to a rel. residual of %.3e after %d iterations in %.2f seconds.' % (1.2323e-3, 1000, 5.1) Out[4]: 'GMRES solver converged to a rel. residual of 1.232e-03 after 1000 iterations in 5.10 seconds.' An exhaustive list of options can be found in section `5.6.2. String Formatting Operations: `_ of the python documentation. Formatting tables with ``str.format()`` (Optional) -------------------------------------------------- This section is optional and mainly intended as a reference for formatting plain text tables. The ``str.format()`` is an alternative way in python for string formatting, it replaces `fields` contained in a string marked with curly braces ``{}``. A `field` is specified by the following syntax: :: {[keyword] : [alignment][format specifier]} The format specifier is identical to the previous section, except that the ``%`` has to be omitted now. Valid alignment options are ``>``, ``<``, ``^``, resp. left, right and center. - Example: .. sourcecode:: ipython In [1]: '{Class:5s} has {NStudents:d} students'.format(Class='Numerische Methoden', NStudents=200) Out[1]: 'Numerische Methoden has 200 students' ``keyword`` is optional and can be omitted completely or replaced by the argument number: .. sourcecode:: ipython In [43]: '{:5s} has {:d} students'.format('Numerische Methoden', 200) Out[43]: 'Numerische Methoden has 200 students' - Example: formatting a table with 3 columns and proper alignment .. sourcecode:: ipython In [2]: title = '{0:>15s}\t{1:>15s}\t{2:>15s}'.format('Refinement','Mesh width', 'Abs. error') ...: print(''.join(len(title)*['-'])) # separator '-------' ...: print(title) ...: print(''.join(len(title)*['-'])) ...: for i in xrange(5): ...: h = 2**(-i) ...: err = h**5 ...: row = '{0:15d}\t{1:15.2f}\t{2:15.2e}' ...: print(row.format(i, h, err)) ...: print(''.join(len(title)*['-'])) Out [2]: ----------------------------------------------- Refinement Mesh width Abs. error ----------------------------------------------- 0 1.00 1.00e+00 1 0.50 3.12e-02 2 0.25 9.77e-04 3 0.12 3.05e-05 4 0.06 9.54e-07 ----------------------------------------------- For more options see section `7.1.3. Format String Syntax `_ of the python documentation. Some nice extra specialities (Optional) --------------------------------------- python - list comprehensions: Often we want to construct lists in a very systematic way, for example a list of all square numbers. This can be done easily by a so called `list comprehension`: .. sourcecode:: ipython In [5]: L = [k**2 for k in xrange(10)] In [6]: L Out[6]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] It is even possible to attach a condition, for example we can compute squares of even numbers only: .. sourcecode:: ipython In [83]: L = [k**2 for k in xrange(10) if k%2 == 0] In [84]: L Out[84]: [0, 4, 16, 36, 64] python - generators: The python documentation states: They [generators] are written like regular functions but use the ``yield`` statement whenever they want to return data. Each time ``next()`` is called, the generator resumes where it left-off [...] Let's just make a trivial example: .. sourcecode:: ipython In [37]: def squares(n): ....: for i in xrange(n): ....: yield i**2 ....: In [38]: S = squares(10) In [39]: S.next() Out[39]: 0 In [40]: S.next() Out[40]: 1 In [41]: S.next() Out[41]: 4 In [42]: S.next() Out[42]: 9 Until we reach the end, where it raises a ``StopIteration`` exception .. sourcecode:: ipython In [48]: S.next() Out[48]: 81 In [49]: S.next() ^[[A --------------------------------------------------------------------------- StopIteration Traceback (most recent call last) in () ----> 1 S.next() StopIteration: This way we can iterate over generated values, and for example combine this with a list comprehension to find the first square numbers: .. sourcecode:: ipython In [55]: L = [s for s in squares(10)] In [56]: L Out[56]: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] Generators can be useful to represent infinite data structures lazily. For example we can compute *all* square numbers, one after the other: .. sourcecode:: ipython In [71]: def squares(): ....: i = 0 ....: while True: ....: yield i ....: i = i+1 ....: In [72]: S = squares() In [73]: S.next() Out[73]: 0 In [74]: S.next() Out[74]: 1 In [75]: S.next() Out[75]: 2 ad infinitum ... python - Anonymous functions (lambda functions): A `lambda function` is a (usually small) function without an explicit name. The are defined like regular function but use the ``lambda`` keyword instead of ``def`` and have no name. Let's look at an example: .. sourcecode:: ipython In [90]: lambda x,y : x + y - 2 Out[90]: > Of course we can assign this function object to any variable name we like: .. sourcecode:: ipython In [91]: f = lambda x,y : x + y - 2 This is now equivalent to a regular definition but involes less typing: .. sourcecode:: ipython In [55]: def f(x, y): ....: return x+y-2 ....: Lambda functions have a few restrictions, for example we can not do any variable assignment or control blocks inside. But still it's possible to do some fancy stuff: .. sourcecode:: ipython In [59]: f = lambda n: [i for i in xrange(n)] In [60]: f(10) Out[60]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Finally, the Zen of Python (hat you might find useful outside of Pyhton, too): .. sourcecode:: ipython In [35]: import this The Zen of Python, by Tim Peters Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. Special cases aren't special enough to break the rules. Although practicality beats purity. Errors should never pass silently. Unless explicitly silenced. In the face of ambiguity, refuse the temptation to guess. There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch. Now is better than never. Although never is often better than *right* now. If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea. Namespaces are one honking great idea -- let's do more of those! More on the Python language --------------------------- You can find more on Python in on-line tutorials and books. I enjoyed *Learning Python* by M. Lutz and *Python in a Nutshell* by A. Martelli. There as the very good official python tutorial covering most of the important stuff and also some things not so important for us: http://docs.python.org/2/tutorial/ You should definitely read at least the following three chapters: - `An Informal Introduction to Python `_ Covers all the basic information that did not fit into this tutorial document. - `More Control Flow Tools `_ Control structures like ``if`` blocks, ``for`` and ``while`` loops and function definitions. - `Data Structures `_ The basic data structures like lists, tuples, dictionaries (hash tables) and sets. For the ones of you interested in object-oriented programming in the python laguage, there is also a chapter on classes and inheritance. - `Classes `_ Covers the topics related to object-oriented programming in python. Finally, python comes with a huge standard library included. - `The Python Standard Library `_ Reference for the python standard library. And if this is still not enough, there is a central storage called `PyPI` containing thousands of python packages for almost any job - `PyPI - the Python Package Index `_ A python package for any job.