You can perform all operation interactively from the Python or IPython prompt. By default, it will output the result, so there is no need for some print
command.
In [ ]:
# Multiplication
2 * 3
In [ ]:
# Division (floating point - default behavior in python3)
8 / 3
In [ ]:
# integer (floor) division
8 // 3
In [ ]:
# Multiplication of strings
'abc' * 5
In [ ]:
# Power operator
3 ** 4
In [ ]:
# Modulo (remainder)
13 % 2
everything after the # symbol is a comment
Python variables are dynamically typed and upcasting is happening:
In [ ]:
a = 3 # int
b = 3. # float
print(a, type(a))
print(b, type(b))
Upcasting is the mechanismn which transforms the type of a variable when needed:
In [ ]:
a = 1
print(a, type(a))
In [ ]:
a = a*0.1
print(a, type(a))
In [ ]:
c = a + b
print(c, type(c))
In [ ]:
boolean = True
print(boolean, type(boolean))
Types can also be used to explicitly cast variables.
convert an integer to a float:
In [ ]:
a = float(3)
print(a, type(a))
convert a string to an integer:
In [ ]:
a = int('3')
print(a, type(a))
convert a float to a string:
In [ ]:
a = str(3.3)
print(a, type(a))
To recover memory, all Python variables can be deleted. However this is hardly ever necessary because Python is a managed language with its own garbage collector.
In [ ]:
del(c, boolean)
In [ ]:
print(c) # raises an exception
Some names can not be used with Python
Everything in python is an object, with attributes and methods. You can access them with a .
after the variable name.
Using IPython a tab will show you the list of attributes and methods of a given object:
In [ ]:
a = 1 # Even int are objects
print(type(a))
In [ ]:
print(a.real) # access the real attribute
print(a.imag) # access the imaginary attribute
In [ ]:
# define a simple function
def foo():
"""a dummy function that returns 1"""
return 777
print(foo())
A list in Python is a set of ordered objects. It can be created empty:
In [ ]:
l = []
print(type(l))
# another equivalent syntax for creating a list
ll = list()
print(type(ll))
and later populated:
In [ ]:
l.append('abxy') # using the append method of python list object
print(l)
or its elements can be defined at once:
In [ ]:
l = [10, 5.,'cc','dd','ff']
print(l)
As you see, an intrinsic python list can mix different types
You can also remove the last element using the 'pop' method:
In [ ]:
last_element = l.pop() # it can also be used as l.pop() without the assignement
print(last_element)
print(l)
Accessing elements is simple, remember python is 0-indexed
In [ ]:
l[2] # Accessing the third element (python is 0-indexed)
In [ ]:
l[-2] # Accessing the second-last element
In [ ]:
l[1] = True # setting the value of the second element
print(l)
Slicing, i.e. taking a subsample of a list, it is very powerful in python. The syntax is list[start : stop : step]
where start
and stop
are indices and step
is a number of elements to jump ahead. By default, start = 0, stop = end and step = 1
NOTE: the stop
argument is the index of the last element that is not included in the slice.
In [ ]:
l[2:] # from 3rd element until the end
In [ ]:
l[:2] # from the start up to the 3rd element (not included)
In [ ]:
l[:-2] # from start to the end minus 2
In [ ]:
l[1::2] # select every other elemnt starting from the second element
In [ ]:
l[::-1] # will reverse the list (same as l[-1:-5:-1])
Concatenation of list is done using the + operand
In [ ]:
a = [1, 2]
b = [3, 4]
c = a + b + [10, 11, 4]
print(c)
A 'tuple' is a special type of object, useful to pack and unpack variables together; it is mostly used in returning arguments of functions. Tuples are immutable (i.e. they are read-only, once they are created, they can not be modified).
In [ ]:
a = (2, 'a', 10.1) # a way to simply pack ...
print(a)
In [ ]:
index, name, value = a # ... and unpack several values together
print(index)
print(name, value)
In [ ]:
a[1] = 5 # this will raise an exception since tuples are immutable
Dictionaries are associative arrays, kind of un-ordered lists, that you can access using keys, close to IDL struct. Keys can be strings, numbers or objects.
In [ ]:
mydict = {'first': 1, 'second':[2, 3], 2: 23}
print(mydict)
In [ ]:
mydict['first'] # will access the element with key first
In [ ]:
mydict.keys() # will return the list of keys
In [ ]:
mydict.values() # will returns of the values
In [ ]:
mydict['fourth'] = 4 # will add a new key to the dictionnary
print(mydict)
dictionaries can be created also by passing arguments to dict()
In [ ]:
my_other_dict = dict(first=1, second=[2, 3], third=['a', 'b', 'c'])
print(my_other_dict)
NOTE ordered dictionaries do exist in Python. They can be found in the module collections
https://docs.python.org/3/library/collections.html
In [ ]:
from collections import OrderedDict
my_ordered_dict = OrderedDict() # create an ordered dict instance
my_ordered_dict['first'] = 1
my_ordered_dict['second'] = [2, 3]
my_ordered_dict['third'] = ['a', 'b', 'c']
print('ordered:', my_ordered_dict)
print('default', my_other_dict)
Strings in Python act as lists of characters.
In [ ]:
my_str = 'abc'
print(my_str[0], my_str[2])
In [ ]:
my_new_str = mystr + 'efg'
print(my_new_str)
In [ ]:
name = 'Tom'
age = 46
size = 1.783
Using the %
method, you must specify the type of the variable withing the string, e.g. %s
for a str, %f
for a float, etc.
In [ ]:
s = "%s is %d years old and is %.2f m tall" # for every number, the precision can be specified ; two decimals here
print(s % (name, age, size))
The .format()
method is aware of the variable type and does the casting.
In [ ]:
s2 = "{} is {} years old and is {:.2f} m tall"
print(s2.format(name, age, size)) # Tuple unpacking
which is equivelant to:
In [ ]:
s2 = "{name} is {age} years old and is {size:.2f} m tall"
print(s2.format(name='Tom', size=1.783, age=46))
It can work with dictionaries keys
In [ ]:
dct = {'name': 'Tom',
'age': 46,
'size': 1.78}
s3 = "{name} is {age} years old and is {size:1.2f} m tall, but let's recall his age : {age}"
print(s3.format(**dct)) # dictionary unpacking
joining strings is easy
In [ ]:
print(' '.join(['a', 'b', 'xyz']))
print('-'.join(['a', 'b', 'xyz']))
All variables are objects in python and assignement only gives a reference. Thus there is a small subtility compared to other programming language:
In [ ]:
a = [1, 2, 3] # a gets its own reference (identity) in memory
b = a # beware that you are assigning references in memory here, so b points to a content
print('{}\n{}'.format(id(a), id(b))) # actual memory references, they are the same
changing an entry in a will change the same entry in b as they are effectively the same object
official documentation for id can be found here
In [ ]:
a[2] = 4 # thus changing a...
print(a, b) # ... will change b
In [ ]:
a = a + [2, 3, 4] # Changing the full object will create a new id,
print(a, b)
To avoid this behavior you can copy an object using the copy module (see later for explanation on module)
In [ ]:
# This will import the copy module, see later for explanation
import copy
c = copy.copy(a)
a[0] = 99
print('', a, '\n', c)
Python offer a easy was to dump and reload (serialize) variable to disk, it is provided by the pickle
or cPickle
module, whose usage is similar to the SAVE/RESTORE command from IDL
In [ ]:
import pickle
a, b = 1, 2.
pickle.dump((a, b), open('myfile.pickle', 'wb'))
In [ ]:
!ls *.pickle
In [ ]:
(c, d) = pickle.load(open('myfile.pickle', 'rb'))
print(c)
print(d)
In [ ]:
a = 2
b = 4
print(a == b) # The equality test will return a boolean
In [ ]:
print(a > b, a >= b)
In [ ]:
print(a < b, a <= b)
In [ ]:
print(a != b)
You can also use the in
statement to test if an element is present in a list
In [ ]:
print(1 in [1, 2, 3])
print('a' in ['a','b','c'])
print('z' in 'abc')
In [ ]:
print ('z' not in 'abc' ) # not is used to inverse the test
True vs False vs None
In [ ]:
print(True is True)
In [ ]:
print(True is False)
In [ ]:
print(True is None)
In [ ]:
print(False is None)
Blocks of instructions are defined by an indentation. Indentation can be made of white spaces or tabs, but you can not mix the two.
It is best pratice to use a multiple of 4 spaces by convention
In [ ]:
if a == 2:
print('Yes') # Define a block of code with 4 withe space
Of course, several tests can be combined all at once
In [ ]:
a, b = 1, 5
if (a == 1) and (b >=4):
print('Good')
else:
print('Not so good')
There is no case
statement in Python, but you can use the elif structure
In [ ]:
if a == 1:
print('No')
elif a == 2:
print('Yes 2')
else:
print('A lot more')
Loops can be made in Python with for
or while
statements
In [ ]:
i = 0
while i < 3:
print(i)
i += 1
A for
loop in Python uses objects it can iterate on, like lists or strings, or generators.
In [ ]:
words = ['cool', 'powerful', 'readable'] # a list
for word in words: # the for statement use list to go through
print("python is " + word)
In [ ]:
for char in 'abc': # Remember strings are list of characters
print(char.upper())
In [ ]:
# generators
print(range(10)) # range is a generator, a python function which return a list of int.
print(range(5,10))
print(range(3,9,3))
In [ ]:
for i in range(2, 8, 2): # range can be used with the for statement to loop over indexes
print(i, 'Hello')
Several lists can be combined in a for
loop (up to the lowest length)
In [ ]:
words = ['cool', 'powerful', 'readable']
superwords = ['fun', 'stable', 'easy']
for word1, word2 in zip(words, superwords):
print( "Python is " + word1 + " and " + word2 )
Using the enumerate
function, you can also generate an index for a given list
In [ ]:
for i, word in enumerate(words): # you may indexes over the list as well as the items
print("%i - python is %s" % (i, word))
Function are really usefull when you need to repeat a task several times. They can have two types of arguments
You can call the function either with the values of the arguments in the declaration order, or use keyword declaration without specific order, however non-keyword arguments should always come before the keyword arguments.
In [ ]:
def do_nothing(): # simplest function ever with empty body.. doesn't do anything and returns None
pass
In [ ]:
print(do_nothing())
In [ ]:
print(do_nothing.__name__)
In [ ]:
def disk_area(radius, pi=3.14):
return pi*radius**2
In [ ]:
print(disk_area(2))
print(disk_area(2, pi=3.14159))
print(disk_area(pi=3.14, radius=2))
In [ ]:
arguments = {'pi': 3.14, 'radius': 2} # Function arguments can be packed into a dictionnary
print(disk_area(**arguments)) # and unpacked at the function call
also functions are objects with attributes. You can access these attributes and even add attributes at runtime:
In [ ]:
print('The name of the function is "%s"' % disk_area.__name__)
Add an attribute to the function:
In [ ]:
# this raises an exception as the attribute 'info' does not exist
print(disk_area.info)
add the attribute info
In [ ]:
disk_area.info = 'this function computes the area of the area of a circle'
print(disk_area.info)
Function documentation a.k.a docstrings
In [ ]:
def foo2(a, b, x=None, y=2, z=[], *args, **kwargs):
"""This is the docstring of the function. There are several ways to
document the parameters. For this function, we use the sphinx convention.
:param a: This is the first positional argument. It is for ....
:param b: This is the seconds positional arugmet. It must be a ..
:param str x: A keyword argument for .... its default value is None
:param int y: A keyword argument for ...
:param iterable z: yet another keyword argument...
:param args: arguments list
:param kwargs: keyword arguments list
"""
print('a = ', a)
print('b = ', b)
print('y = ', y)
print('z = ', z)
print('args = ', args)
print('kwargs = ', kwargs)
In [ ]:
foo2?
In [ ]:
foo2(1, 2, x='table', z=['g', 'h'], k1='fff', k2='iii')
More explanation of the usage of args and kwargs can be found here
A module is a series of usefull functions or classes put together, by you or other. It is very simple to import modules into Python, in order to use the functions it provides. They are several ways to import a module. Each module provide their own namespace, i.e. a way to recognize them. It is always a good idea to keep all module functions into their own namespace to avoid conflict and allow good tracability.
For example, the default python math
module provide mathematical functions. We will see later that other module provide similar functions. It is then a good idea to keep the namespace distinct to know who's who.
In [ ]:
import math # Import all the math module in its own namespace
print(math.sqrt(2))
In [ ]:
import math as m # Import all the math module and name it as m
print(m.sqrt(2))
In [ ]:
from math import sqrt as mysqrt # Import only the math.sqrt function as mysqrt
print(mysqrt(2))
In [ ]:
# This should be avoided unless you know exactly what you are doing
from math import * # Import all the math functions into the current namespace (could have name conflict)
print(sqrt(2))
open a file and write some stuff to it
In [ ]:
fobj = open('myfoo.txt', 'w')
for word in 'i hope it is not raining outside'.split():
fobj.write('{}\n'.format(word))
fobj.close()
In [ ]:
!cat myfoo.txt
open the file back (in read mode) and print its content
In [ ]:
with open('myfoo.txt') as fobj_read:
for line in fobj_read:
print(line)
the with statment safely opens and closes the file when pickling is finished. It is called a context manager (for more info click here)
In [ ]:
import math
print(math.__doc__) # will print the docstring of the module
In [ ]:
help(math) # will produce a nice output of the help
?math # will do the same in IPython
In [ ]:
help(math.sqrt) # will print the help of the math.sqrt function
Each file defines a module namespace. Thus a disk.py file defines the disk module. You can see a module as a container for functions and/or variables.
In [ ]:
%%file disk.py
# When need to use a double value for pi, found in the math module
from math import pi
def area(radius=1.): # By default the radius is 1.
return pi*radius**2
def length(radius):
return 2*pi*radius
In [ ]:
import disk
In [ ]:
print(disk.area()) # By default we said the radius would be 1
print(disk.area(2)) # This call the area function from the loaded disk module
In [ ]:
from disk import area as disk_area
print(disk_area(2)) # This call the disk_area function imported from the disk module
A script is a serie of more or less complex python commands that you can easily run
In [ ]:
%%file disk_script.py
import disk as mydisk
from disk import area as disk_area
print(mydisk.area(2))
print(disk_area(3))
In [ ]:
%run disk_script.py # runs the script in IPython
In [ ]:
import disk # This import a module
import disk_script # Will import a script as a module and thus
# run its content (on the first import only),
# but this does not actually really make sense...
In [ ]:
print(disk_script.mydisk.area(2)) # and leads to syntax like that
In [ ]:
import importlib
importlib.reload(disk) # is needed if you want to re-import a modified version of an already imported module
The simplest class
In [ ]:
# define the class
class myclass():
pass
# create an instance
foo_instance = myclass()
print(type(foo_instance))
A more useful class that defines some attributes
In [ ]:
class myclass():
x = 1 # this is a class attribute
y = 2 # this is another class attribute
print(myclass.x)
print(myclass.y)
An even more useful class that defines methods
In [ ]:
class myclass():
x = 1 # class attribute
y = 2 # another class attribute
def func1():
print('The value of x is:', myclass.x)
def func2(self):
print('The value of x is:', self.x)
myclass.func1() # call the method
foo_instance = myclass() # create an instance
myclass.x += 1 # increment the class attribute x
foo_instance.func2() # print the incremented attribute value
The class attrubute value is shared between the instance and the class itself.
So far we have been using classes as namespaces that provide access to their own attributes and methods. Next we will define classes with controlled initialization where eacy instance has its own attributes.
In [ ]:
class myclass(object): # inherit object
def __init__(self):
self.x = 1 # attribute created upon creating an instance
self.y = 2 # another attribute created upon creating an instance
print('created a new instance of myclass', self)
def func1(self):
print('The value of x is:', self.x)
def increment_x(self):
self.x += 1
In [ ]:
my_instance = myclass() # create an instance
In [ ]:
# do a method call on the instance
my_instance.func1()
In [ ]:
my_instance.increment_x()
my_instance.func1()
In [ ]:
print('The attributes and method of the class are', dir(myclass))