print
writes strings and values
separate arguments with ,
print(1, 2, 3)
print(4)
print("done")
One line comments:
# this is a comment
a = 3 # and this is another comment
No declaration of variable types, just assign values. Type of variable is determined from value on the right side of =
:
a = 1.23
print(a * a)
Check type of a variable with built in type
function:
print(type(a))
b = 4711 * 42
print(type(b))
c = "I heart Python"
print(type(c))
_
_
or digits# those are fine:
a_b_c = 1
a123 = 2
_aXzA = 3
+
, *
, -
, /
and parenthesis as usual, **
for exponentiation:
print(2 * (3 + 4) - 7)
exponentiation is **
not ^
:
print(2 ** 10)
print(13 / 4)
%
for modulo (aka division reminder) computation:
print(13 % 4)
x = 2 ** 62 # this can be represented by a 64 bit integer.
y = 2 ** 63 # this overflows in 64 bit integers
print(x, y)
Can be used to compute 1000 digits of pi: http://stackoverflow.com/questions/9004789/1000-digits-of-pi-in-python
There is no distinction between single or double precision floats, the Python float
type is always with double precision, but may overflow:
print(2.0 ** 1000)
print(2.0 ** 1500)
math
module¶import
to use them.import math
print(math)
Now functions and constanstants are "attached" to math
:
print(math.pi)
print(math.sin(1.0))
Python help system:
print(help(math)) # lots of output
print(help(sum))
Alternative ways to import functions, values, etc:
from math import pi, e, sin, log
print(log(e))
from math import *
works, it imports everything (which might be a lot), but is dangerous, for example this overwrites a variable e
:
e = 123
from math import *
print(e)
input
¶name = input("what is your name ? ")
print("hi", name, "how do you do ?")
print(type(name))
input
always returns a string. To work with numbers we need type conversion.
E.g.:
print(float("1.23"))
print(int("42"))
So if we want the user to enter a floating point number and we want to work with the input as number, we write:
x = float(input("give me a number: "))
print(x, "squared is", x * x)
print(42)
and run this scriptStrings are defined using delimiters "
or '
or """
or '''
:
If you choose "
as delimiter you may use '
in the string and the other way round.
print("hi, it's time to go")
print('this is "a quote"')
long = """multi line string ...
it works"""
print(long)
The repr
function gives us more detailed information:
print(repr(long))
# this is a single line comment
print(3)
"""
this is a multi line comment
the comment ends here
"""
print(4)
print("3.1" + '41')
print(3 * "\o/ ")
print(len("12345"))
Many string operations are "attached" to string object.
Python strings are immutable ("const"). So string methods never change the string object in place. So for example the following upper
method creates a new string:
# transforms string "hello" to a new string:
greeting = "hello"
print(greeting.upper())
print(greeting) # unchanged !
Method calls can be chained. For example this startswith
method ...
print("hi you".startswith("hi"))
... can be called on the result of upper()
:
print("hi you".upper().startswith("HI"))
Use [..]
for accessing parts of a string, counting start with 0
.
print("Python"[1])
Negative indices start at the end, -1
is the last character, -2
the character before the last character and so on:
print("Python"[-2])
substrings using [m:n]
, the right limit is exclusive:
print("Python"[2:4])
print("Python"[1:-1])
help(str.rstrip)
etc for looking up the used methods. If you are sure that you know what the output will be, use Python to validate your results.values = "012" * 3 + """'a'bc"""
a = values[0:2] + values[0] + values[2:3]
b = a + values[len(values) - 2].upper()
c = a.rstrip('2')
d = a.find("A")
Use
def <name>(<arg_0>,...):
<body>
to define a Python function:
def times_3(x):
print("you called me with arguemnt", x)
return 3 * x
times_3(42)
NO BRACES: As soon as identation decreases the body of the function is completed.
No type declaration for the arguments. During execution of the function Python determines if operations on arguments fit:
print(times_3("ab"))
def sum_and_diff(x, y):
sum_ = x + y
diff = x - y
return sum_, diff
a, b = sum_and_diff(7, 3)
print("sum is", a)
print("diff is", b)
If you use a string directly after the def ...
line we call this string a "doc string" which allows us to contribute to the built in help system:
def average_3(a, b, c):
"""this function computes the average of
three given numbers
"""
return (a + b + c) / 3.0
help(average_3)
Python has a type bool
which can take two values True
and False
:
ok = True
print(ok, type(ok))
Logical values result from comparing numbers:
notation | meaning |
---|---|
a < b |
a is less than b |
a > b |
a is greater than b |
a <= b |
a is less than or equal to b |
a >= b |
a is greater than or equal to b |
a == b |
a is is equal to b |
a != b |
a is not equal to b |
Comment:
=
(which is variable assignment) is a statement ("it does something")==
(which is test for equality) is an expression (it can be evaluated to compute a value)Logical values can be combined
notation | meaning |
---|---|
a and b |
True if a and b are True |
a or b |
True if a or b are True |
not a |
True if a is False else False |
print(3 > 4 or 4 > 3)
print(3 < 7 and 7 < 12)
if
, elif
and else
keywords for branching code execution. def test_if_even(x):
if x % 2 == 0:
print(x, "is even")
else:
print(x, "is odd")
test_if_even(12)
Identations can be nested:
def some_tests(x):
if x > 0:
if x % 2 == 0:
print(x, "is positive and even")
else:
print(x, "is positive and odd")
elif x == 0:
print(x, "is zero")
else:
print(x, "is negative")
some_tests(4)
some_tests(-1)
4
for input 2
and 3
for input 3
.Python has while
, continue
and break
:
x = 7
while x > 0:
x = x - 1
if x % 2 == 0:
continue # skips rest of body of while
print(x)
if x % 3 == 0:
break # quit body of while
print("done")
Python has some container types for collecting values. list
is one such a type:
li = [1, 2, 4, 8]
print(li)
length of a list:
print(len(li))
print(type(li))
The empty list is []
:
print(type([]))
print(len([]))
List of strings:
li = ["hi", "ho"]
Mixed types:
li = [1, 2.0, True, "hi"]
print(li)
More about lists below.
for
loops¶Loops with for
can loop over so called "iterables" for example over lists.
for name in ["urs", "uwe", "guido"]:
print("I say hi to", name)
def sumup(li):
sum_ = 0.0
for item in li:
sum_ += item # same as sum_ = sum_ + item
return sum_
print(sumup([1, 2, 3]))
For implementing a "counting loop" use range
which returns an iterable:
for i in range(3):
print(i, "squared is", i * i)
Python lists collect data, types may be mixed and the list may be as long as your computers memory allows.
Some usefull, but not all list methods:
li = [1, 2, 3]
li.append(0)
print(li)
print(len(li))
li.sort()
print(li)
print(min(li), sum(li), max(li))
List slicing, similar to strings:
print(li[1:-1])
In contrast to Python strings, which are immutable ("const"), you can use slicing for deletion and replacement of parts of a list as follows:
del li[1:3]
print(li)
li[1:3] = [777, 888, 999]
print(li)
"immutable" lists. Use paranthesis instead of brackets:
a = (1, 3, 5)
print(a)
print(type(a))
Rules of thumb:
tp = (1, 2, (1, 2), "")
print(tp[1:-1])
Empty tuple is ()
, for one element tuples use (x,)
notation:
print((1,)) # prints tuple with one element
print((1)) # prints integer number 1
Dictionaries, aka "hash tables" or "look up tables" allow presentation of two column tables.
For example:
surname | name |
---|---|
monty | python |
curt | cobain |
surnames = { 'monty': 'python',
'curt' : 'cobain',
}
print(surnames)
You see above that printing the dictionary has a different order than in its definition. Dictionaries are only for representing a mapping from values in the left column of the table to their counterpart in the right column. Ordering is not respected.
To lookup up a value use brackets:
print(surnames["monty"])
You can put values into a dictionary like this:
surnames["uwe"] = "schmitt"
print(surnames)
Size of a dictionary:
print(len(surnames))
Left column of table are "keys":
print(surnames.keys())
Right column are "values":
print(surnames.values())
Both are iterables again:
for name in surnames.keys():
print(name)
The empty dictionary is {}
:
d = {}
print(d)
print(len(d))
Lookup of non existing keys:
print(surnames["jesus"])
print(surnames.get("monty"))
print(surnames.get("jesus"))
Dictionaries may have different types for keys and values:
dd = { 3: 9, 4: "four", 5: {25: 125}, 6: (1,2), 7:range(10), 8: None}
print(dd)
print(dd[5][25])
open(path, mode)
returns an file object which you can use for reading and writing. mode
maybe "r"
, "w"
, "a"
(or some other types...) for "reading", "writing" and "appending":
fh = open("say_hi.txt", "w")
fh.write("hi")
# always close a file because data may be in buffer:
fh.close()
This is dangerous if you forget to close the file or if an error resumes programm execution before the close
method is called !
Background: most OS do not write immediatly to disk if you call write
but collect data until an internal memory region (buffer) is filled. So you never now exactly what is still in the buffer and what is on disk. Only after closing or calling fp.flush()
you can be sure that your data is on disk.
with
:¶Since Python 2.5 we have the with
statement. This statement executes the following body in a secure way, so that the file is always closed, even in case of an error inside the body.
with open("hiho.txt", "w") as fh:
fh.write("hi")
fh.write("ho")
#file content of hiho.txt
with open("say_hi.txt", "w") as fh:
print("say", file=fh)
print("hi", file=fh)
The line below is not Python code and is only supported by the software I used for this tutorial. It prints the content of the file named "say_hi"
, so you see the result from the snippet above:
#file content of say_hi.txt
The read
method returns the full content in one string, we open the file in read mode "r"
here:
with open("say_hi.txt", "r") as fh:
print(repr(fh.read()))
read
without arguments reads the full file. So if you call read
on a file containg 1 MB of data you get a very long string.
But for text files there are two more comfortable ways to read: readlines
returns the file line by line in a list of strings:
with open("say_hi.txt", "r") as fh:
print(fh.readlines())
Comment: there is also a method called readline
(no s
at the end !) which only reads one line. So take care the use the right method name.
What is nice in Python is that you can loop over the lines in a file using for
:
with open("say_hi.txt", "r") as fh:
for line in fh:
print(repr(line))
Performance tip: use the for
loop for iterating over a file. For huge files this only reads as much bytes as needed in every iteration and thus works for files which are larger than your computers memory !
This is not covered in this course, but look at https://pymotw.com/3/csv/index.html !
Serialisation writes (even nested) data structures in a binary format to a disk. You can recover this data later on easily. In Python serialisation is called "pickling" like conserving vegatables in a jar.
# this is a complex data structure:
data = (1, { 1: 2, 3: [1, 2, 3], "s": (1, 2)})
print(data)
import pickle
with open("data.bin", "wb") as fh: # open in "write binary" mode.
pickle.dump(data, fh)
# this is how it looks like on disk:
#file content of data.bin
# now recover your complex data structure:
with open("data.bin", "rb") as fp:
recovered = pickle.load(fp)
print(recovered)
print(recovered == data)
1, 4, 9, ..., 100
line by line to a text file, check the content with your file system explorer then write some code to read the numbers again.split
method, use a dictionary for countingThis section assumes that you already had an introduction to object oriented programming in some programming language and will show how classes work in Python.
A class in Python is declared with the class
statement, methods are defined with def
like functions.
Remember: a class
is a "recipe" or "template" for the creation of objects. Or the other way round: a object is an instance of a class
.
import math
class Vector2D:
def __init__(self, x0, y0):
self.x = x0 # set attribute x
self.y = y0 # set attribute y
def length(self):
"""method which computes length of Vector2D"""
return math.hypot(self.x, self.y)
# create an instance, this creates the object and
# calls __init__ with args 1 and -1:
p = Vector2D(1, -1)
# now we can access attributes
print(p.x, p.y)
# and call method
print(p.length())
class Vector2D:
provides the name of your class and the following indented code block decares the methods of the class.
__init__
it the initializer method which is called if you instantiate Vector2D(1, -1)
.
It sets the attributes x
and y
.
There is no type declaration for attributes, you just set them inside __init__
.
There are no rules for "private" or "protected" attributes and methods. A user of a class can access every attribute and method.
But: it is common practice to use names starting with a single _
for private attributes and methods.
self
is the current instance of the object. (In C++ / Java we have this
instead).
In Python you have to use self
as the first parameter when you declare methods, but you do not provide it if you call the method.
Methods are attached to the class, so for examle you can access
print(Vector2D.__init__)
print(Vector2D.length)
If you call p.length()
Python looks up the class of p
which is Vector2D
and translates this to Vector2D.length(p)
, so self
is set to p
if you call Vector2D.length
and so the computation works on attributes of p
.
There are many other special methods having names starting and ending with __
like __init__
. For example __str__
is called whenever you convert a object to a string representation. This is the case when you print(the object:
import math
class Vector2D:
def __init__(self, x0, y0):
self.x = x0
self.y = y0
def length(self):
return math.hypot(self.x, self.y)
def __str__(self):
return "Vector2D(x=%s, y=%s, lenght=%s)" % (self.x, self.y, self.length()) # attribute and method access here !
p = Vector2D(1, -1)
# this calls the __str__ method:
print(p)
Or you can implement __add__
which is called if you write p1 + p2
for two instances of Point
:
import math
class Vector2D:
def __init__(self, x0, y0):
self.x = x0
self.y = y0
def length(self):
return math.hypot(self.x, self.y)
def __str__(self):
return "Vector2D(x=%s, y=%s, length=%s)" % (self.x, self.y, self.length()) # attribute and method access here !
def __add__(self, other):
""" is called when you execute self + other """
return Vector2D(self.x + other.x, self.y + other.y)
p = Vector2D(1, -1)
q = Vector2D(1, 1)
# now call p.__add__(q) which is
# the same as Vector2D.__add__(p, q):
print(p + q)
There are many other special functions for customizing your objects, see https://docs.python.org/2/reference/datamodel.html#special-method-names
Say we want to create a class which inherits Vector2D
by giving it an extra attribute name
. This can be implemented as follows:
class NamedVector2D(Vector2D):
def __init__(self, name, x0, y0):
super().__init__(x0, y0) # call __init__ in base class
self.name = name
def __str__(self):
""" overrides __str__ from Vector2D """
return "NamedVector2D(name=%s, x=%s, y=%s)" % (self.name, self.x, self.y)
# create instance
v1 = NamedVector2D("v1", 1.0, 2.0)
# access attributes
print(v1.x, v1.y, v1.name)
# calls __str__:
print(v1)
# calls method in base class !
print(v1.length())
# this is in base class too, see result:
print(v1 + v1)
Vector2D
to implement a method scale
which takes a float x
and scales the attributes x
and y
internally.__mul__
which takes another vector and returns the dot product (scalar product) of both. __mul__
is called if you use v1 * v2
.ComplexNumber
which inherits Vector2D
and reimplements __mul__
for complex arithmethic. Reimplement __str__
to achieve output in the style of 1.0 + 2i
.Tuple unpacking allows taking values from a tuple without index access, so you need less code and it is often more readable. Instead of writing
tp = (1, 2, 3)
a = tp[0]
b = tp[1]
c = tp[2]
print(a + b + c)
... you can write:
a, b, c = tp
print(a + b + c)
You can ommit parantheses for declaring a tuple:
a, b, c = 1, 2, 3
print(a + b + c)
Good style: use __
for not needed values if you do tuple unpacking. So if you only want to unpack a
use the following style to avoid declaration of variables you will not use later on:
tp = (1, 2, 3)
a, __, __ = tp
Tuple unpacking is handy for exchanging values, you need no temporary variables:
print(a, b)
a, b = b, a
print(a, b)
If this was to fast, here is a more detailed implementation:
print(a, b)
tp = (b, a) # creates tuple
a, b = tp # unpacks the values and overwrites variables "a" and "b" with new values !
print(a, b)
Before we demonstrate tuple unpacking in for
statements, we introduce the zip
function which takes 2 or more lists and "zips" the elements to a list of tuples similar to a zipper on your jacket:
a = [11, 22, 33]
b = ["a", "b", "c"]
print(zip(a, b))
print(list(zip(a, b)))
So zip
iterates over tuples. Tuple unpacking with for
now does tuple unpacking for every tuple in the given list:
for num, char in zip(a, b):
print(num, char)
If you want to iterate over a list and you want to count at the same time enumerate
is handy:
for i, char in enumerate(b):
print(i, char)
List comprehensions allow creation and transformation of lists in a comprehensive and readable way. For example the following two lines ...
squares = [i * i for i in range(6)]
print(squares)
... are equivalent to
squares = []
for i in range(6):
squares.append(i * i)
print(squares)
But you can filter too:
squares_of_odds = [i * i for i in range(6) if i % 2 == 1]
print(squares_of_odds)
squares_of_odds = []
for i in range(6):
if i % 2 == 1:
squares_of_odds.append(i * i)
print(squares_of_odds)
We used range
only for demonstration, you take any other iterable instead:
words = ["hi", "this", "is", "list", "comprehension"]
print([w.upper() for w in words if len(w) % 2 == 0])
try
/ except
/ finally
¶Python throws exceptions in case of errors:
x = 1 / 0
But you can "catch" exceptions:
try:
x = 1 / 0
except ZeroDivisionError:
print("oops")
You can raise your own exceptions:
import math
def fun(number):
if number < 0.0:
raise Exception("%s is negative! " % number)
return math.sqrt(number)
print(fun(2.0))
If you provoke an exception, you see a so called stack trace which indicates how this error was triggered:
# read the output below line by line !!!
print(fun(-1.0))
Exceptions can be handled on different levels:
def test(x):
return fun(x - 1)
for i in range(3):
try:
print(test(i))
except:
print("test failed for argument", i)
Functions may have default values:
def call(a, b=3, c=4):
print("a =", a, ", b =", b, "and c =", c)
call(1, 2, 3)
call(1, 2)
call(2)
Why ? Good for sensible default values (e.g. tolerances or iteration counts in numerical algorithms)
You can name the arguments when you call a function:
call(1, c=5)
call(c=5, a=3, b=4)
Why ? Meaning of parameters becomes obvious if you read the function call, no need to look up the function definition.