Useful informations

  • Variables are not typed, while objects are.
  • Variables address objects (strong typing).
  • Variable names are best in camelCase (first letter in lower case and no numbers in first) and as meaningful as possible.
  • In Python to identify a block of instructions, you need an identation of 4 spaces and not a tab
  • Small immutable objects are sometimes stored as singletons (only one possible occurrence)

Basic functions

Function Description Type argument(s) Output
help() Help on a function Function name String
type() Type of a variable All Type
len() Length All Int
print() Print to screen All Exit screen
input("Question Text") Retrieves value entered by user String All

Types of data

Type Example Immutable? Singleton? Notes
Integer 1 Immutable Possible No loss of accuracy
Boolean True or False Immutable Possible No loss of accuracy
Float 1.2 Immutable Possible Possible loss of accuracy
Complex 1+2j Immutable Possible Possible loss of accuracy
String "blah" or 'blah' Immutable Possible
Tuple ( ,) or ( , ,) or , Immutable Possible
List [ , ] Mutable Stores only the references
Dictionary { : , : } Mutable Unordered and unchangeable keys/values
Set { , } Mutable Unordered and unique immutable keys

Operators

Operator Name Notes
= Opérateur d'affectation
# Comments
+ Addition or concatenation Int + Int = Int Int + Float = Float Text + Text = Text
- Subtraction Int - Int = Int Int - Float = Float
* Multiplication Int * Int = Int Int * Float = Float
/ Division Int / Int = Float Int / Float = Float
% Modulo Subtract from division
// Whole division Rounded down
** Power idem pow(a,b)
abs() Absolute value
str() Cast in String str(Int) = String
int() Cast in Int int(Float) = Int
float() Cast in Float float(Int) = Float
+= Simultaneous addition and affection

Logic operators

Sign Description
== equal to
!= different
< strictly less than
<= lower than or equal to
not() not
and or & and
or or 'pipe' or
^ or exlusive
element in myGroup is the element in my group

Strings (immutable)

Method Description Argument(s) Output
objectString.upper() All capitalized - Text
objectString.capitalize() First letter uppercase, subsequent letters lowercase - Text
objectString.strip() Removes spaces before and after the string - Text
objectString.replace("old", "new") Replaces all occurrences of old with new Text, Text Text
objectString.find("look") Returns the position of the first occurrence of the searched word or -1 Text Int
objectString.strip(',').split(',') Split put each word in a list according to a fixed character (",") and thanks to strip the last "," will be deleted - List
",".join(['abc', 'def']) Makes a string from a list according to a fixed character (e.g. here ",") - Text

f string

f"blah-blah-blah {function1(variable1)} blah-blah-blah {function2(variable2)} blah-blah-blah"
age = 45
name = "Paul"
gap = 10
f"In {gap} year(s), {name} will be {age + gap} year(s) old."
'In 10 year(s), Paul will be 55 year(s) old.'

List (mutable)

nameList = ["String", number, otherList, variable]

The list does not store the objects but only the reference to the object. Its size will not vary according to the size of the referenced objects.

The index starts at 0.

Unlike text, they are modified by their methods => they are mutable.

Use Method Description
Index myList[1] Accesses the 2nd element of the list
myList[-1] Go to the last element of the list
myList[1:3] Accesses the 2nd and 3rd elements of the list
myList.index(element) Returns the index of the element
Classic print(myList) Displays the entire list
len(myList) Returns the length of a list
Sorting myList.sort() Changes myList into a sorted list instead
sorted(myList) Copy and sort myList
myList.copy() Shallow copy
Add myList.append(element) Adds the element to the end of the list
myList.insert(index, element) Add the element to the specified index and move the following elements by one index
myList.extend([otherList]) Add the list otherList to the end of the first one (myList)
Remove myList.pop(index) Delete and display the element with the index as argument or the last one
maListe.remove(element) Deletes the first occurrence of an element
del myList[index] Deletes the element at the specified index
String " ".join(myList) Transforms a list into a string according to the " ", the opposite of split()
Random random.choice(myList) choose an element from the list
random.choices(myList,3) choose 3 elements in the list with throw-in
random.sample(myList,3) choose 3 elements from the list without throw-in
myList = [8, 5, 12, 4, 45, 7]
myList1 = [8, 5, 12, 4, 45, 7]
myList2 = myList1 # The reference to the objects are copied!
myList1[0] = 0
myList2
[0, 5, 12, 4, 45, 7]
myList[1] = 5
sum(myList) = 81
max(myList) = 45
myList = list(range(2))
myList = [0, 1]
myList.append(2)
myList = [0, 1, 2]
myList.insert(1,"one half")
myList = [0, 'one half', 1, 2]
myList = [3, 5, 7, 6, 4, 1, 0, 2]
myList.sort()
myList = [0, 1, 2, 3, 4, 5, 6, 7]

Tuple (immutable)

       (element,) 
    or  element, 
    => tuple singleton

       (element, element) 
    or (element, element,)    <= for writing long tuples on several lines
    or  element, element     
    or  element, element,
    => multiple tuple

These are sequence objects.

They have the same properties as lists except that they are immutable => once created they cannot be modified.

(a, b) = [3, 4]
a = 3
b = 4
a, b = b, a
a = 4
b = 3
e = list(range(10))

# tuple unpacking : cut a list
# with the * we indicate that the length is unknown
# with the _ or "ignored" we indicate that we are not interested in the content of this variable

x, *_, y = e
x = 0
y = 9
a = [1, 2]
b = [3, 4]
z = zip(a,b)
[i for i in z]
[(1, 3), (2, 4)]

Dictionarie (mutable - immutable keys)

myDictionary = {key1:value1, key2:value2}
age = {"eve":30, "ana":25, "bob":35}

listeTupleAge = [("hel",21), ("jon",41)]
dicoAge = dict(listeTupleAge)

age.update(dicoAge)
{'eve': 30, 'ana': 25, 'bob': 35, 'hel': 21, 'jon': 41}
age['eve']
30
age.keys()    # the result is a view
dict_keys(['eve', 'ana', 'bob', 'hel', 'jon'])
age.values()  # the result is a view
dict_values([30, 25, 35, 21, 41])
for name,year in age.items():
    print(f"{name.capitalize()} is {year} years old")
Eve is 30 years old
Ana is 25 years old
Bob is 35 years old
Hel is 21 years old
Jon is 41 years old

Set (mutable - unordered)

{key1, key2}

A set is essentially a hash table. Close to dictionaries, mutable, unordered, they store only unique keys and are optimized for membership testing.

a = [1, 2, 1, 20, 1]
s = set(a)
{1, 2, 20}
s.add(10)
{1, 2, 10, 20}
s.update([1, 3, 30])
{1, 2, 3, 10, 20, 30}
s.discard(30)
{1, 2, 3, 10, 20}
30 in s
False

If

if condition:
    block of instructions

elif condition:
    block of instructions

else:
    block of instructions

In conditional expression

Variable = valueTrue if test else valueFalse

In a test :

- False : False, 0, None, [], {}, (), ""
- True : everything else
userName = "C"

if len(userName)==1:
    print("Hello single!")
    
elif len(userName)>0:
    print("Hello", userName)
    
else:
    print("Hello World!")
Hello single!
y = "Hello single!" if (len(userName) == 1) else "Hello World!"
print(y)
Hello single!

Range(n)

range(*start, end + 1, *step)

     range(0, 5, 1) 
idem range(0,5) 
idem range(5) 
=> 0, 1, 2, 3, 4
x = range(2, 7, 2)
for n in x:
    print(n)
2
4
6

For

for element in myGroup:
    instructions

for i in range(start, end + 1, step):
    instructions

With the continue keyword, you can indicate that you want to go directly to the next element of the loop. With the keyword break, we interrupt the loop.

Note: with the function exit(), we can stop the programme
myList = [0, 3, 7, 13]

for element in myList:
    print(element)
0
3
7
13
myText = "World"

for element in myText:
    print(element)
W
o
r
l
d

While

while condition:
    instructions
    continue      # ignore the following statements for this iteration
    break         # exit while

while True:       # infinite loop
    instructions
    break
a = list(range(1,7))

print(a)

while a:
    a.pop()
    if len(a) == 5:
        continue # ignore les instructions suivantes pour cette itération
    print(a)
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4]
[1, 2, 3]
[1, 2]
[1]
[]

Class

class ClassName():
    """
    documentation on several lines
    """
    def __init__(self, p_argument1):
        self.argument1 = p_argument1

    (...)

    def __str__(self):
        return "argument1 = " + str(self.argument1)

Important: a class is the mold of an object, it represents the essence of a concept or a physical object. It defines both the data characterizing this object and the actions (functions) that this object performs.
Note: a class (the mold) is instancied = an object is created according to the mold
class Ship:
    def __init__(self, model, hp, armor, damage):
        self.model = model
        self.hp = hp
        self.armor = armor
        self.damage = damage
        
    def __str__(self):
        return self.model + ", hp:" + str(self.hp) + ", armor:" + str(
            self.armor) + ", damage:" + str(self.damage)
    
# Creation of an instance of this class
ship1 = Ship("CRUISER", 1200, 100, 100)

# Print out this instance
print("ship1: ", ship1)

# Creation of an instance of this class
ship2 = Ship("FIGHTER", 300, 10, 1000)

# Print out this instance
print("ship2: ", ship2)
ship1:  CRUISER, hp:1200, armor:100, damage:100
ship2:  FIGHTER, hp:300, armor:10, damage:1000

Functions

def functionName(arguments):
    """
    documentation on several lines
    """
    instructions (with or without return or just pass)

Document well if the function modifies the parameter passed to it

def calculPerimetreTriangle (cote1, cote2, cote3):
    """
    receives three side lengths
    and returns the sum of the three
    """
    perimetre = cote1 + cote2 + cote3
    return perimetre

print(calculPerimetreTriangle(1,2,3))
6

You can put an optional argument with default value (not a mutable object)

def functionName(argument1, argument2, argument3=defaultvalue3):
    instructions
def f(a, b=10):
    print(a, b)

f(1)
f(1, 2)
1 10
1 2

Tuple or dictionaries in arguments

To not have to specify the number of elements sent to the function:

#Tuple
def f(*t):
    print(t)

#dictionary
def f(**d):
    print(d)
def f(*t):
    print(t)
    
f(1)
f(1, 2, 3)
(1,)
(1, 2, 3)
def f(a, b):
    print(a, b)

L = [1, 2]
f(*L)
1 2
def f(**d):
    print(d)
    
f(nom="Dupond", prenom="Paul")
{'nom': 'Dupond', 'prenom': 'Paul'}

Named arguments

When you call the function, you can pass the arguments in the order you want, if you name them:

functionName(nameArgument2=valueArgument2, nameArgument1=valueArgument1)

Warning: In the function call, it is not possible to enter a positional argument after an argument named
def f(a, b):
    print("a =", a,"b =", b)
    
f(b=1, a=5)
# but
f(1, 5)
# warning
#f(b=1, 5) => SyntaxError: positional argument follows keyword argument
a = 5 b = 1
a = 1 b = 5

Order of consumption of arguments

The 4 forms of arguments will be summarized, depending on how the arguments of the function have been written and how they are called :

  1. all "normal" arguments, called positional
  2. form *args which catches in a tuple the rest of the positional arguments
  3. named arguments name=<value>
  4. form **dargs which catches in a dictionary the remainder of the named arguments
def foo(positionalArg1, namedArg2=100, *tuplePositionalArg, **dictNamedArg):
    print(f"positionalArg1={positionalArg1}, namedArg2={namedArg2}, *tuplePositionalArg={tuplePositionalArg}, **dictNamedArg={dictNamedArg}")
foo(1)
positionalArg1=1, namedArg2=100, *tuplePositionalArg=(), **dictNamedArg={}
foo(1, 2)
positionalArg1=1, namedArg2=2, *tuplePositionalArg=(), **dictNamedArg={}
foo(1, 2, 3)
positionalArg1=1, namedArg2=2, *tuplePositionalArg=(3,), **dictNamedArg={}
foo(1, 2, 3, 4, arg5=5, arg6=6)
positionalArg1=1, namedArg2=2, *tuplePositionalArg=(3, 4), **dictNamedArg={'arg5': 5, 'arg6': 6}
def bar(positionalArg1, *tuplePositionalArg, namedArg2=100, **dictNamedArg):
        print(f"positionalArg1={positionalArg1}, namedArg2={namedArg2}, *tuplePositionalArg={tuplePositionalArg}, **dictNamedArg={dictNamedArg}")
bar(1)
positionalArg1=1, namedArg2=100, *tuplePositionalArg=(), **dictNamedArg={}
bar(1, 2)
positionalArg1=1, namedArg2=100, *tuplePositionalArg=(2,), **dictNamedArg={}
bar(1, 2, namedArg2=3)
positionalArg1=1, namedArg2=3, *tuplePositionalArg=(2,), **dictNamedArg={}
bar(1, 2, 3, namedArg2=4, namedArg5=5)
positionalArg1=1, namedArg2=4, *tuplePositionalArg=(2, 3), **dictNamedArg={'namedArg5': 5}

Lambda functions

Functions that we do not intend to reuse

lambda x: [function of x]

You can use a lambda to access a characteristic of an object for a sort

myList.sort(key=lambda x: x.characteristic1)

You can execute a lambda function on a list of arguments (x) with a map and see the results in a list with a list:

list(map(lambda x: [function of x], [list of x]))

and filter the results with a filter:

list(filter(lambda x: [test on [function of x]], [list of x]))
list(map(lambda x: x**2, range(10)))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
list(filter(lambda x: x % 2 == 0, range(10)))
[0, 2, 4, 6, 8]
list(map(lambda x: x**2, filter(lambda x: x % 2 == 0, range(10))))
[0, 4, 16, 36, 64]

List comprehensions = For on lists

We create a list, set or dictionary according to our needs.

on l a list

comprehesion_l = [ function(i) for i in l if test(i) ]

on s a set

comprehesion_s = { function(i) for i in s if test(i) }

on d a dictionary

comprehesion_d = { function(i): j for i,j in d.items() if test(i, j) }
a = [1, 4, 6, 9, 13]
b = [i**2 for i in a]
[1, 16, 36, 81, 169]
name = ['Alice', 'evE', 'sonia', 'BOB']
name = [p.title() for p in name]
['Alice', 'Eve', 'Sonia', 'Bob']

Iterator

Generator expression

iterableI = (function(x) for x in list)

Simple and compact object that allows to browse an iterable object.

The iterable object contains the data, the iterator object contains the mechanism to traverse it.

They can be browsed only once. We recognize an iterator because it is its own iterator :

i is iter(i) => True

They have two methods:

* __iter()__
* __next()__
square = [x**2 for x in range(1_000)]
227 µs ± 12.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
square2 = (x**2 for x in range(1_000))
515 ns ± 38.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
palindrome = (x for x in square2 if str(x) == str(x)[::-1])
list(palindrome)
[0,
 1,
 4,
 9,
 121,
 484,
 676,
 10201,
 12321,
 14641,
 40804,
 44944,
 69696,
 94249,
 698896]
generator = (x**2 for x in range(10**18) if x%17==0) 
recherche = set()
for x in generator:
    if x > 10**10:
        break
    elif str(x)[-4:] == '1316':   # with a generator, you only pay for what you use
        recherche.add(x)
{617721316, 4536561316, 3617541316, 311381316}

Generator function

def functionName (arguments):
    instructions
    yield valueReturn

Function that returns an iterable so no memory space is used

def gen(it):
    for i in it:
        if isinstance(i, int):
            yield i**2
        else:
            yield 'nan'
            
L = [1, 2, 0, '18', 'x', [11], 25]
[1, 4, 0, 'nan', 'nan', 'nan', 625]

Modules and packages

A module is a Python file (.py), a package is a set of Python files (+ init.py).

The namespace is an attribute of the module.

Packages and modules are imported in the same way at the top of NoteBook

import moduleName as md
import packageName as pk

md.variableName
md.functionName
md.className
pk.filename.variableName

=> perfect isolation of namespaces


Import with from (more complex) :

from nomModule import nomVariable

=> risk of collision between the local variable nameVariable and the imported one
=> no access to other attributes of moduleName

Package random

Method Use
random.random() a float between 0 and 1 (1 not included)
random.uniform(a, b) a float between a and b (b not included)
random.randint(a, b) an integer between a and b (included) random.gauss(average)
import random
print(random.random())
print(random.uniform(1,2))
print(random.randint(1,3))
0.11548597942353367
1.134093036662287
3
import random
population = range(1000) 

# Number of tickets desired 
N = 10
random.sample(population,N)
[932, 62, 122, 223, 623, 970, 217, 261, 535, 156]
def div(a,b):
    try:
        print(a/b)
    except ZeroDivisionError:
        print("oops, division by 0")
    except TypeError:
        print("oops, integers needed")
div(5,2)
2.5
div(5,0)
oops, division by 0
div("cinq",2)
oops, integers needed

Exceptions

try:
    expression0
except nomException1:
    expression1
except nomException2:
    expression2
else:               # on passe si aucune exception est attrapée (on n'y passe pas s'il y a un return avant)
    expression3
finally:            # on passe dans le finally même s'il y a un return avant
    expression4

Mistrust on the floats

0.3 - 0.1
0.19999999999999998
0.3 - 0.1 == 0.2
False
from decimal import Decimal

Decimal('0.3') - Decimal('0.1') == Decimal('0.2')
True
from fractions import Fraction

Fraction(3, 10) - Fraction(1, 10) == Fraction(2, 10)
True

Time mesurement

import datetime
start_time = datetime.datetime.now()
print(f"Start: {start_time} microseconds")
Start: 2021-05-18 19:12:12.206309 microseconds
print(f"Delay: {datetime.datetime.now() -  start_time} microseconds")
Delay: 0:00:02.790415 microseconds

Choosing a priority after going through an entire list

priority = 100
choice = None

for l in myList:
    if tests1 and priority > 1:
        choice = "1"
        priority = 1
    if test2 and priority > 2:
        choice = "2"
        priority = 2

print(choice)
myList = [5, 0, 10, 7, 11, 1, 12]
priority = 100
choice = None
    
for l in myList:
    if l > 10 and priority > 1:
        choice = l
        priority = 1
    if l > 2 and priority > 2:
        choice = l
        priority = 2

print(choice)
11