These notes were taken during learning Python programming language.
Learning resources:
- https://realpython.com/python-operators-expressions/
- https://github.com/TheAlgorithms/Python
- http://web.cs.elte.hu/~kiraly/Algoritmusok.pdf
- http://www.youtube.com/watch?v=h9wxtqoa1jY
- https://www.geeksforgeeks.org/graph-coloring-applications/
- http://users.monash.edu/~lloyd/tildeAlgDS/Graph/Undirected/
- https://www.udemy.com/the-python-mega-course/learn/v4/t/lecture/4775338?start=0
- https://github.com/vinta/awesome-python
- https://github.com/keon/algorithms
- https://github.com/faif/python-patterns
- https://www.youtube.com/watch?v=yNzxXZfkLUA&list=PLsyeobzWxl7poL9JTVyndKe62ieoN-MZ3&index=47
- https://www.youtube.com/watch?v=hYzwCsKGRrg&list=PLsyeobzWxl7poL9JTVyndKe62ieoN-MZ3&index=45
- https://www.python-course.eu/python3_passing_arguments.php
- https://medium.com/@meghamohan/mutable-and-immutable-side-of-python-c2145cf72747
- Algorithms and data structures
- Object Oriented Python, Classes, Objects, Inheritance, Method Overloading, Operator Overloading, ...
- Decorators
- Generators
- Iterators
- Closures
- Lambda functions / Anonymous functions
- Modules in Python
- Variable length arguments
- Pass a parameter to a function
- Types of arguments, function arguments
- Exception handling
- Multithreading
- UI: Tkinter
- PostgreSQL
- Desktop app, SQLite
- OpenCV, image processing
- Bokeh: data visualization
- Webscraping: BeautifulSoup
- Tensorflow
- Keras
- Flask
- Django
- Complexity, ordo, etc...
- Searching, Sorting
- Arithmetic with big numbers
- Data Structure: Linked List
- Data Structure: Binary Search Tree
- Data Structure: AVL Tree
- Data Structure: Red-Black Tree
- Data Structure: 2-3 Tree
- Data Structure: B-Tree
- Data Structure: Queue
- Data Structure: Stack
- Data Structure: Graph
- Graph Algorithm: Breadth-first-search
- Graph Algorithm: Depth-first search
- Graph Algorithm: Dijkstra's Shortest Path First algorithm, SPF algorithm
- Graph Algorithm: Floyd–Warshall-algorithm
- Graph Algorithm: Johnson-algorithm
- Graph Algorithm: Suurballe-algorithm
- Graph Algorithm: Graph Coloring - Two Colors of Vertices
- Graph Algorithm: The Minimum Spanning Tree of an Undirected Graph
- Graph Algorithm: PERT method - Topologic Sequence - House Building
- Graph Algorithm: Acyclic Dijkstra
- Graph Algorithm: Bellman–Ford-algorithm
- Graph Algorithm: Matching, Stable Matching, Gale–Shapley algorithm
- Graph Algorithm: Maximal Matching in Bipartite Graphs ("páros gráf" = "bipartite graph"), Hungarian algorithm
- Graph Algorithm: Matching in non-bipartite graphs
- Graph Algorithm: Matching - Hopcroft–Karp-algorithm
- Graph Alhorithm: Network flows - Maximal flow - Ford–Fulkerson algorithm, Edmonds–Karp algorithm
- Graph Alhorithm: Network flows - Dinic-algorithm
- Hashing
- Data Compression, Huffmann
- Arithmetics, multiplication of big numbers with Karacuba method
- Arithmetics, multiplication of big numbers with Schönhage-Strassen method
- Discrete Fourier Transformation, FFT
- Dynamic programming: Optimal binary search tree
- Jún 2: Week 2 programming assignment
- Jún 16: Week 3 full
- Jún 16: Week 4 full
- Jún 16: Week 5 full
- Jún 23: Week 6 full
- Jún 30: Week 7 full
- Júl 7: Week 8 full
- Júl 21: Week 9 full
- Aug 4: Week 10 full
- Aug 18: Week 11 full
- Szept 1: Fundamentals of Deep Learning for Computer Vision
- Szept 15: Fundamentals of Accelerated Computing with CUDA Python
- Szept 29: Fundamentals of Accelerated Computing with CUDA C/C++
- Coursera Deep Learning Specialization: 16 weeks
These are some specialities in Python which were not so common in the programming languages I used so far (eg. ABAP).
How does it work:
or
returns the first that is truthyand
returns the first that is falsy
Example function:
def f(arg):
print("-> f({})".format(arg))
return arg
Tests:
>>> f(0) or f(False) or f(1) or f(2) or f(3)
-> f(0)
-> f(False)
-> f(1)
1
>>> f(0) and f(False) and f(1) and f(2) and f(3)
-> f(0)
0
>>> f(1) and f(2) and f(3) and f(0) and f(False) and f(True)
-> f(1)
-> f(2)
-> f(3)
-> f(0)
0
>>> f(0) or f(False) or f(0.0)
-> f(0)
-> f(False)
-> f(0.0)
0.0
>>> f(0) and f(False) and f(0.0)
-> f(0)
0
>>> string = 'foo bar'
>>> s = string or '<default_value>'
>>> s
'foo bar'
>>> string = ''
>>> s = string or '<default_value>'
>>> s
'<default_value>'
def div(a,b):
print(a/b)
def smart_div(func):
def inner(a,b):
if a < b:
a,b = b,a
return(func(a,b))
return inner
div1 = smart_div(div) # or we can even name the function div also
div1(2,4)
Alternative way of using the decorator: with @ symbol
def smart_div(func):
def inner(a,b):
if a < b:
a,b = b,a
return(func(a,b))
return inner
@smart_div
def div(a,b):
print(a/b)
div(2,4)
# This file is named BasicOperations.py and is in folder Calculation
def add(a, b):
return a+b
def sub(a, b):
return a-b
def mul(a, b):
return a*b
def div(a, b):
return a/b
# This file is named demo.py and is in the root folder of the project
import Calculation.BasicOperations
# alternative way:
# import Calculation.BasicOperations as BasicOps
a = 7
b = 9
c = Calculation.BasicOperations.add(a, b)
d = Calculation.BasicOperations.sub(a, b)
e = Calculation.BasicOperations.mul(a, b)
f = Calculation.BasicOperations.div(a, b)
def main():
print(c)
print(d)
print(e)
print(f)
if __name__ == "__main__":
main()
If you pass immutable arguments like integers, strings or tuples to a function, the passing acts like call-by-value. The object reference is passed to the function parameters. They can't be changed within the function, because they can't be changed at all, i.e. they are immutable. It's different, if we pass mutable arguments. They are also passed by object reference, but they can be changed in place in the function. If we pass a list to a function, we have to consider two cases: Elements of a list can be changed in place, i.e. the list will be changed even in the caller's scope. If a new list is assigned to the name, the old list will not be affected, i.e. the list in the caller's scope will remain untouched.
Immutable types are the following:
- bool
- int
- float
- str
- tuple
- frozenset
These are handled like pass-by-value. The object reference is passed to the function parameters. They can't be changed within the function, because they can't be changed at all, i.e. they are immutable.
It is also important to mention that Python initially behaves like call-by-reference, but as soon as we are changing the value of such a variable, i.e. as soon as we assign a new object to it, Python "switches" to call-by-value. This means that a local variable x will be created and the value of the global variable x will be copied into it.
Consider the following example:
def ref_demo(x):
print("x and id(x) inside the function, before changing it ", x, id(x))
x = 5000
print("x and id(x) inside the function, after changing it ", x, id(x))
if __name__ == "__main__":
x = 500
print("x and id(x) outside the function, before calling the function ", x, id(x))
ref_demo(x)
print("x and id(x) outside the function, after calling the function ", x, id(x))
The output of the above is
x and id(x) outside the function, before calling the function 500 4402783344
x and id(x) inside the function, before changing it 500 4402783344
x and id(x) inside the function, after changing it 5000 4401954768
x and id(x) outside the function, after calling the function 500 4402783344
Mutable types are the following:
- list
- array
- set
- dict
These are handled like pass-by-reference. They are also passed by object reference, but they can be changed in place in the function.
If we pass a list to a function, we have to consider two cases: Elements of a list can be changed in place, i.e. the list will be changed even in the caller's scope. If a new list is assigned to the name, the old list will not be affected, i.e. the list in the caller's scope will remain untouched.
Consider the following example:
def no_side_effects(cities):
print('locations inside method call, before modification', cities)
cities = cities + ["Birmingham", "Bradford"]
print('locations inside method call, after modification', cities)
print("test with \"cities = cities + ['..', '..']\" - no side effects")
locations = ["London", "Leeds", "Glasgow", "Sheffield"]
print('locations before method call', locations)
no_side_effects(locations)
print('locations after method call', locations)
Output:
test with "cities = cities + ['..', '..']" - no side effects
locations before method call ['London', 'Leeds', 'Glasgow', 'Sheffield']
locations inside method call, before modification ['London', 'Leeds', 'Glasgow', 'Sheffield']
locations inside method call, after modification ['London', 'Leeds', 'Glasgow', 'Sheffield', 'Birmingham', 'Bradford']
locations after method call ['London', 'Leeds', 'Glasgow', 'Sheffield']
Consider the following example:
def side_effects(cities):
print('locations inside method call, before modification', cities)
cities += ["Birmingham", "Bradford"]
print('locations inside method call, after modification', cities)
print("test with \"cities += ['..', '..']\" - side effects, because += works as in-place operator")
locations = ["London", "Leeds", "Glasgow", "Sheffield"]
print('locations before method call', locations)
side_effects(locations)
print('locations after method call', locations)
Output:
test with "cities += ['..', '..']" - side effects, because += works as in-place operator
locations before method call ['London', 'Leeds', 'Glasgow', 'Sheffield']
locations inside method call, before modification ['London', 'Leeds', 'Glasgow', 'Sheffield']
locations inside method call, after modification ['London', 'Leeds', 'Glasgow', 'Sheffield', 'Birmingham', 'Bradford']
locations after method call ['London', 'Leeds', 'Glasgow', 'Sheffield', 'Birmingham', 'Bradford']
A shallow copy is sufficient, because there are no nested structures in the list.
def side_effects(cities):
print('locations inside method call, before modification', cities)
cities += ["Birmingham", "Bradford"]
print('locations inside method call, after modification', cities)
print("test with \"cities += ['..', '..']\" - side effects, because += works as in-place operator")
locations = ["London", "Leeds", "Glasgow", "Sheffield"]
print('locations before method call', locations)
side_effects(locations[:]) # shallow copy
print('locations after method call', locations)
Output:
test with "cities += ['..', '..']" - side effects, because += works as in-place operator
locations before method call ['London', 'Leeds', 'Glasgow', 'Sheffield']
locations inside method call, before modification ['London', 'Leeds', 'Glasgow', 'Sheffield']
locations inside method call, after modification ['London', 'Leeds', 'Glasgow', 'Sheffield', 'Birmingham', 'Bradford']
locations after method call ['London', 'Leeds', 'Glasgow', 'Sheffield']
def varpafu(*x):
print('parameters: ', x)
print('type of parameters: ', type(x))
if __name__ == "__main__":
varpafu(12, 'Second text parameter', "Third param", False, 2.43, ["a","b"], ("c","d",34,"e"))
parameters: (12, 'Second text parameter', 'Third param', False, 2.43, ['a', 'b'], ('c', 'd', 34, 'e'))
type of parameters: <class 'tuple'>
The asterisk "*" is used in Python to define a variable number of arguments. The asterisk character has to precede a variable identifier in the parameter list.
Sometimes, it's necessary to use positional parameters followed by an arbitrary number of parameters in a function definition. This is possible, but the positional parameters always have to precede the arbitrary parameters. In the following example, we have a positional parameter "city", - the main location, - which always have to be given, followed by an arbitrary number of other locations:
def locations(city, *other_cities): print(city, other_cities)
locations("Paris")
locations("Paris", "Strasbourg", "Lyon", "Dijon", "Bordeaux", "Marseille")
Paris ()
Paris ('Strasbourg', 'Lyon', 'Dijon', 'Bordeaux', 'Marseille')
There is also a mechanism for an arbitrary number of keyword parameters. To do this, we use the double asterisk "**" notation:
def varpafu(**kwargs):
print('parameters: ', kwargs)
print('type of parameters: ', type(kwargs))
if __name__ == "__main__":
varpafu(a=12, b='Second text parameter', c="Third param", d=False, e=2.43, f=["a","b"], g=("c","d",34,"e"))
parameters: {'a': 12, 'b': 'Second text parameter', 'c': 'Third param', 'd': False, 'e': 2.43, 'f': ['a', 'b'], 'g': ('c', 'd', 34, 'e')}
type of parameters: <class 'dict'>