From 6dcd1727eb9d5dbfcb8f6e9599ada10d33063bac Mon Sep 17 00:00:00 2001
From: Simeon Simeonov
Date: Wed, 18 Jan 2023 21:12:31 +0100
Subject: Remove some old presentations
---
notebooks/python/python_intro.ipynb | 2 +-
notebooks/python/python_oo.ipynb | 107 ++++--
reveal.js/dataaccess.html | 360 -------------------
reveal.js/dataporten.html | 118 -------
reveal.js/demo.html | 4 +-
reveal.js/evalg.html | 316 ++++++++---------
reveal.js/flow.html | 194 ----------
reveal.js/gateway.html | 152 --------
reveal.js/mqprod.html | 484 ++++++++++++-------------
reveal.js/postgresql_tuning.html | 265 +++++++-------
reveal.js/python-oo.html | 145 --------
reveal.js/python.html | 680 ------------------------------------
reveal.js/rabbitmq.html | 528 ++++++++++++++--------------
reveal.js/sqlalchemy.html | 301 ++++++++--------
reveal.js/timetravel.html | 246 ++++++-------
15 files changed, 1151 insertions(+), 2751 deletions(-)
delete mode 100755 reveal.js/dataaccess.html
delete mode 100644 reveal.js/dataporten.html
delete mode 100755 reveal.js/flow.html
delete mode 100644 reveal.js/gateway.html
delete mode 100644 reveal.js/python-oo.html
delete mode 100644 reveal.js/python.html
mode change 100755 => 100644 reveal.js/sqlalchemy.html
diff --git a/notebooks/python/python_intro.ipynb b/notebooks/python/python_intro.ipynb
index 3c49a14..0168839 100644
--- a/notebooks/python/python_intro.ipynb
+++ b/notebooks/python/python_intro.ipynb
@@ -1253,7 +1253,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.10.8"
+ "version": "3.11.1"
}
},
"nbformat": 4,
diff --git a/notebooks/python/python_oo.ipynb b/notebooks/python/python_oo.ipynb
index b56aa26..1c655d0 100644
--- a/notebooks/python/python_oo.ipynb
+++ b/notebooks/python/python_oo.ipynb
@@ -179,7 +179,8 @@
"\"The Zen of Python\" (import this) is still relevant and should be followed :)\n",
"\n",
"\"Python will remain a dynamically typed language, and the authors have no desire to ever make type hints mandatory, even by convention.\" - PEP484\n",
- "\n"
+ "\n",
+ "Class (type) names should normally use the *CapWords* convention - CapitalizedWords (or *CapWords*, or *CamelCase*). This is also sometimes known as *StudlyCaps*. ASCII characters and English names only!"
]
},
{
@@ -225,17 +226,17 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7f5630542810> with id 140008154736656 from with id 94904583728688\"\n",
- "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7f5633ec8190> with id 140008215052688 from with id 94904583728688\"\n",
- "car1.model = 'BMW', car1.reg_nr = 'EC76183', car1.extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140008154734272, id(car1.__init__) = 140008154735680\n",
- "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140008154734272, id(car2.__init__) = 140008154736768\n",
- "id(Car.cls_extras) = 140008154734272, id(Car.__init__) = 140008154484864\n",
- "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140008154734272\n",
- "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140008154734272\n",
+ "car1.get_obj_info_str() = \"I am <__main__.Car object at 0x7fc7add96f10> with id 140495591927568 from with id 94628241379728\"\n",
+ "car2.get_obj_info_str() = \"I am <__main__.Car object at 0x7fc7aef459d0> with id 140495610468816 from with id 94628241379728\"\n",
+ "car1.model = 'BMW', car1.reg_nr = 'EC76183', car1.extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140495591909184, id(car1.get_obj_info_str) = 140495591927808\n",
+ "car2.model = 'Scoda', car2.reg_nr = 'BD77655', car2.extras = ['GPSnav'], id(car2.cls_extras) = 140495591909184, id(car2.get_obj_info_str) = 140495591927936\n",
+ "id(Car.cls_extras) = 140495591909184, id(Car.get_obj_info_str) = 140495591682944\n",
+ "car1.cls_extras = ['GPSnav', 'Sound system'], id(car1.cls_extras) = 140495591909184\n",
+ "car2.cls_extras = ['GPSnav', 'Sound system'], id(car2.cls_extras) = 140495591909184\n",
"True\n",
"hasattr(car1, 'import_tax_paid') = True\n",
"hasattr(car2, 'import_tax_paid') = False\n",
- "id(car1.__class__) = 94904583728688, id(car2.__class__) = 94904583728688, id(Car) = 94904583728688\n"
+ "id(car1.__class__) = 94628241379728, id(car2.__class__) = 94628241379728, id(Car) = 94628241379728\n"
]
}
],
@@ -244,6 +245,7 @@
"\n",
" cls_extras = [] # class attribute - shared by all instances\n",
" # cls_extras: list = [] is also possible\n",
+ " car_count = 0\n",
"\n",
" def __init__(self, model: str, reg_nr: str, extras: list):\n",
" \"\"\"\n",
@@ -262,24 +264,40 @@
"\n",
" # self.cls_extras = extras # will create a *NEW instance attribute* called 'cls_extras'\n",
" \n",
+ " Car.car_count += 1 # increase Car.car_count by 1 every time a car is created \n",
+ " \n",
+ " def __del__(self):\n",
+ " \"\"\"\n",
+ " Called when the instance is about to be destroyed.\n",
+ "\n",
+ " This is also called a finalizer or (improperly) a destructor.\n",
+ " \"\"\"\n",
+ " Car.car_count -= 1 # decrease by 1 every time a Car object is destroyed\n",
+ "\n",
" def get_obj_info_str(self): # regular method that becomes instance attribute\n",
" return f\"I am {self} with id {id(self)} from {self.__class__} with id {id(self.__class__)}\"\n",
"\n",
"car1 = Car(\"BMW\", \"EC76183\", [\"GPSnav\", \"Sound system\"])\n",
"car2 = Car(\"Scoda\", \"BD77655\", [\"GPSnav\"])\n",
"\n",
- "print(f\"{car1.get_obj_info_str() = }\")\n",
+ "print(f\"{car1.get_obj_info_str() = }\") # same as calling Car.get_object_info_str(car1)\n",
"print(f\"{car2.get_obj_info_str() = }\")\n",
"\n",
"# default behavior for `object.attr`:\n",
"# getter:\n",
"# - checks if 'attr' is an instance attribute\n",
- "# - checks if 'attr' is a class attribute\n",
+ "# - checks if 'attr' is a class attribute (through the method resolution order - MRO)\n",
"# - raises AttributeError\n",
- "# setter: (re)defines an instance attribute\n",
- "print(f\"{car1.model = }, {car1.reg_nr = }, {car1.extras = }, {id(car1.cls_extras) = }, {id(car1.__init__) = }\")\n",
- "print(f\"{car2.model = }, {car2.reg_nr = }, {car2.extras = }, {id(car2.cls_extras) = }, {id(car2.__init__) = }\")\n",
- "print(f\"{id(Car.cls_extras) = }, {id(Car.__init__) = }\")\n",
+ "# setter:\n",
+ "# - (re)defines an instance attribute\n",
+ "\n",
+ "# Details not covered in this course:\n",
+ "# Car.x is translated to Car.__dict__[\"x\"] (...through the MRO)\n",
+ "# car1.x is translated to car1.__dict__[\"x\"] (if not found ... Car.x (see above), if not found car1.__getattr__(\"x\") is called)\n",
+ "\n",
+ "print(f\"{car1.model = }, {car1.reg_nr = }, {car1.extras = }, {id(car1.cls_extras) = }, {id(car1.get_obj_info_str) = }\")\n",
+ "print(f\"{car2.model = }, {car2.reg_nr = }, {car2.extras = }, {id(car2.cls_extras) = }, {id(car2.get_obj_info_str) = }\")\n",
+ "print(f\"{id(Car.cls_extras) = }, {id(Car.get_obj_info_str) = }\")\n",
"car1.cls_extras.extend(car1.extras)\n",
"# car1.cls_extras = car1.extras # N.B. This will create a *NEW instance attribute* called 'cls_extras'\n",
"print(f\"{car1.cls_extras = }, {id(car1.cls_extras) = }\")\n",
@@ -289,7 +307,7 @@
"car1.import_tax_paid = True # 'import_tax_paid' will only be present in car1\n",
"print(car1.import_tax_paid)\n",
"print(f\"{hasattr(car1, 'import_tax_paid') = }\") # Out: True\n",
- "print(f\"{hasattr(car2, 'import_tax_paid') = }\") # Out: False\n",
+ "print(f\"{hasattr(car2, 'import_tax_paid') = }\") # Out: False - no 'import_tax_paid' attribute in object car2\n",
"\n",
"# .__class__ instance attribute pointing to its class (type) will be created\n",
"print(f\"{id(car1.__class__) = }, {id(car2.__class__) = }, {id(Car) = }\")"
@@ -705,6 +723,7 @@
"name": "stdout",
"output_type": "stream",
"text": [
+ "2:2\n",
"__mul__ called\n",
"vector * 3 = Vector(start=Point(x=2, y=2), end=Point(x=12, y=18))\n",
"__rmul__ called\n",
@@ -771,6 +790,7 @@
" # __rmul__ = __mul__\n",
"\n",
"vector = Vector(Point(2, 2), Point(4, 6))\n",
+ "print(f\"{vector['start']}\")\n",
"print(f\"{vector * 3 = }\")\n",
"print(f\"{3 * vector = }\")"
]
@@ -863,7 +883,7 @@
"id": "d1d17f26-de6e-4510-b0f0-3ae02761ae24",
"metadata": {},
"source": [
- "# Example 2\n",
+ "# Example 3\n",
"\n",
"We will create a small and incomplete Animal class hierarchy"
]
@@ -973,7 +993,7 @@
" super().__init__(\n",
" weight=weight, tooth_replacement=True, alive=alive, **kwargs\n",
" )\n",
- " self_conservation_status = conservation_status\n",
+ " self._conservation_status = conservation_status\n",
"\n",
"\n",
"class Cow(Mammal):\n",
@@ -1104,6 +1124,7 @@
" \"\"\"\n",
"\n",
"\n",
+ "# not good! The user of JSONMixin should not have to explicitly know about / use DictMixin\n",
"class JSONConvertableTigerShark(TigerShark, DictMixin, JSONMixin):\n",
" \"\"\"A tiger shark class that provides JSON conversion functionality\"\"\"\n",
"\n",
@@ -1143,14 +1164,7 @@
"MRO is monotonic when the following is true: if C1 precedes C2 in the linearization of C, then C1 precedes C2 in the linearization of any subclass of C.\n",
"\n",
"Not all classes admit a linearization. There are cases, in complicated hierarchies, where it is not possible to derive a class such that its linearization respects all the desired properties. \n",
- "`TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y` is raised then.\n",
- "\n",
- "\n",
- "## The Liskov substitution principle (LSP):\n",
- "\n",
- "Functions that use references to base class objects must be able to use objects of derived classes without knowing it.\n",
- "\n",
- "In other words: an object (such as a class) may be replaced by a sub-object (such as a class that extends the first class) without breaking the program.\n"
+ "`TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y` is raised then."
]
},
{
@@ -1232,6 +1246,14 @@
"help(simeon)"
]
},
+ {
+ "cell_type": "markdown",
+ "id": "875e7ffd-4feb-4ef0-b912-0a939c6c2dd1",
+ "metadata": {},
+ "source": [
+ "... **N.B.** The most important thing to note here is that if `super()` is used in `Grandfather1` for this MRO (f.i. object of type `Simeon`) , none of its parents or siblings is called, but `Father` that was probably \"not even born\" when `Grandfather1` was created. :)"
+ ]
+ },
{
"cell_type": "code",
"execution_count": 12,
@@ -1274,7 +1296,6 @@
" \"\"\"Produces JSON content for production plans\"\"\"\n",
" \n",
" def to_json(self, **kwargs):\n",
- " # super().get_data(**kwargs)\n",
" self.get_data(**kwargs)\n",
" prod_plans = self.extract_production_plans(**kwargs)\n",
" return json.dumps(self.__do_some_magic(prod_plans), indent=kwargs.get(\"indent\", 2))\n",
@@ -1295,7 +1316,10 @@
"\n",
"\n",
"cool_ts_proprocessor = CoolTSPreprocessor()\n",
- "print(cool_ts_proprocessor.to_json(indent=4))"
+ "print(cool_ts_proprocessor.to_json(indent=4))\n",
+ "\n",
+ "# but we want to test CoolTSPreprocessor without the annoying ConnectionManager.get_data method being used\n",
+ "# simply (temporary or canditionally) replacing ConnectionManager with MockConnectionManager will go against the O in SOLID"
]
},
{
@@ -1311,6 +1335,7 @@
"Using mock data\n",
"Extracting production plans from data\n",
"some magic is happening here...\n",
+ "{}\n",
"(, , , , )\n"
]
}
@@ -1320,10 +1345,34 @@
" pass\n",
"\n",
"mocked_but_still_cool_ts_preprocessor = MockedButStillCoolTSPreprocessor()\n",
- "mocked_but_still_cool_ts_preprocessor.to_json(indent=4)\n",
+ "print(mocked_but_still_cool_ts_preprocessor.to_json(indent=4))\n",
"print(MockedButStillCoolTSPreprocessor.__mro__)"
]
},
+ {
+ "cell_type": "markdown",
+ "id": "738b9084-27b9-4837-a927-e2087267d483",
+ "metadata": {},
+ "source": [
+ "# Quick tips and final words\n",
+ "\n",
+ "- Avoid the classical pitfall: \"I am solving simple problems by simple means (the way I am used to) and could probably use OO when solving more complex problems\"! Solving simple problems is the best way to get used to the OO way of thinking / designing code.\n",
+ "\n",
+ "- Take some time to analyze and design your solution and discuss it with other team members and more experienced programmers. Do not rely only on QA (merge requests)!\n",
+ "\n",
+ "- Learn to think in terms of objects, like in real world objects (f.i. Car, Animal, TimeSeries, ProductionPlan, DataSource...)!\n",
+ "\n",
+ "- Learn to think in terms of abstract classes (types): Classes that will only serve as a foundation to their child classes, but will themselves never be directly instanciated!\n",
+ "\n",
+ "- Do not create \"thin\" classes / objects that simply containin some relevant attributes, protected by corresponding properties! The entire functionality (business logic) of the object should be encapsulated there.\n",
+ "\n",
+ "- Always make sure that you are not violating some of the SOLID principles (and others described above)! Run \"mental tests\" to test your current design!\n",
+ "\n",
+ "- Consider composition (\"has-a\" relationship) before considering inhiritance (\"is-a\" relationship)!\n",
+ "\n",
+ "- Read and learn from free software projects written in Python (f.i. on github.com)"
+ ]
+ },
{
"cell_type": "markdown",
"id": "540de317-ba77-4f5c-97ba-f1c0bec071fb",
diff --git a/reveal.js/dataaccess.html b/reveal.js/dataaccess.html
deleted file mode 100755
index 4bafccd..0000000
--- a/reveal.js/dataaccess.html
+++ /dev/null
@@ -1,360 +0,0 @@
-
-
-
-
- SQLAlchemy & Odin Data Access
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
SQLAlchemy & Odin Data Access
-
Fifty Data Science
-
-
Simeon Simeonov
-
-
-
-
-
-
Agenda
-
-
-
SQLAlchemy - Design & overview
-
SQLAlchemy - A small practical example
-
Odin Data Access - Overview and examples
-
Q & A
-
-
-
-
-
-
-
What is SQLAlchemy?
-
-
SQLAlchemy is a Python library created by Mike Bayer to provide a high-level Pythonic interface to RDBMS such as PostgreSQL, SQLite, MySQL, Oracle, DB2.
-
SQLAlchemy includes RDBMS-independent SQL expression language and an object-relational mapper (ORM).
-
-
-
-
Why use SQLAlchemy?
-
-
-
portability - the programming interface is independent of the type of RDBMS and connector used
-
security - no more SQL injections
-
abstraction - no need to bother with complex JOINs
-
object-orientation - you work with objects and NOT tables and rows
-
performance - exploits the likehood of reusing a particular query
-
flexibility - you can override almost anything
-
-
-
-
-
Basic architecture
-
SQLAlchemy consists of several components, including the the ORM.
-
-
Engine- manages the connection pool and the RDBMS-independent SQL dialect layer
-
MetaData - used to collect and organize information about your table layout (schema)
-
Session - establishes all conversations with the RDBMS and represents a "holding zone" for all the objects which you've loaded or associated with it during its lifespan
-
SQL expression language - provides an API to execute your queries and updates against your tables, all from Python, and all in a database-independent way (low-level interface)
-
ORM - provides a convenient way to add database persistence to your Python objects withoutrequiring you to design your objects around the database, or the database around the objects (high-level interface)
-
-
-
-
-
-
Example
-
SQLAlchemy gives us the choice between classical mapping and the newer declarative mapping
-
-
- import sqlalchemy
- sqlalchemy.__version__
-
- from sqlalchemy import create_engine
-
- # engine = create_engine("postgresql+psycopg2://user:zipassword@localhost/mydb" , echo=True)
- # The string form of the URL is dialect+driver://user:password@host/dbname[?key=value..],
- # where dialect is a database name such as mysql, oracle, postgresql, etc.,
- # and driver the name of a DBAPI, such as psycopg2, pyodbc, cx_oracle
- # The echo flag is a shortcut to setting up SQLAlchemy logging,
- # which is accomplished via Python’s standard logging module.
- # With it enabled, we’ll see all the generated SQL produced.
- # engine = create_engine("sqlite:///library.db", echo=True)
- engine = create_engine("sqlite:///:memory:", echo=True)
-
- metadata = MetaData()
-
- from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
-
- authors_table = Table(
- "authors",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("name", String),
- ) # Column("name", String(50)) is possible
-
- books_table = Table(
- "books",
- metadata,
- Column("id", Integer, primary_key=True),
- Column("title", String),
- Column("description", String),
- Column("author_id", ForeignKey('authors.id')),
- )
-
- metadata.create_all(engine) # creates the tables
-
-
-
-
-
-
Example (cont...)
-
Use of SQL expression language
-
-
- insert_stmt = authors_table.insert(bind=engine)
- type(insert_stmt)
- # Out: <class 'sqlalchemy.sql.expression.Insert'>
- print(insert_stmt)
- # Out: INSERT INTO authors (id, name) VALUES (:id,:name)
-
- compiled_stmt = insert_stmt.compile()
- print(compiled_stmt.params)
- # Out: {'id': None, 'name': None}
-
- insert_stmt.execute(name="Alexandre Dumas") # insert a single entry
- insert_stmt.execute([{"name": 'Mr X'},{'name': 'Mr Y'}]) # a list of entries
-
- metadata.bind = engine # no need to explicitly bind the engine from now on
- select_stmt = authors_table.select(authors_table.c.id==2)
- result = select_stmt.execute()
- result.fetchall()
- # Out: [(1, u'Mr X')]
-
- del_stmt = authors_table.delete()
- del_stmt.execute(whereclause=text("name='Mr Y'"))
- del_stmt.execute() # delete all
-
-
Doing the same thing the easy way with declarative mapping
-
-
- from sqlalchemy.ext.declarative import declarative_base
- from sqlalchemy.orm import relationship, backref
-
- Base = declarative_base()
-
- class Author(Base):
- __tablename__ = "authors"
-
- id = Column(Integer, primary_key=True)
- name = Column(String)
-
- def __init__(self, name):
- self.name = name
-
- def __str__(self):
- return self.name
-
-
- class Book(Base):
- __tablename__ = "books" # self.__table__ will be available for our objects
-
- id = Column(Integer, primary_key=True)
- title = Column(String)
- description = Column(String)
- author_id = Column(Integer, ForeignKey("authors.id"))
- author = relationship(Author, backref=backref("books", order_by=title))
-
- def __init__(self, title, description, author):
- self.title = title
- self.description = description
- self.author = author
-
- def __str__(self):
- return self.title
-
- Base.metadata.create_all(engine)# create tables
-
-
-
-
-
-
Example (cont...)
-
Creating instances
-
-
- from sqlalchemy.orm import sessionmaker
-
- Session = sessionmaker(bind=engine) # bound session
- session = Session()
-
- author_1 = Author("Richard Dawkins")
- author_2 = Author("Matt Ridley")
-
- book_1 = Book("The Red Queen", "A popular science book", author_2)
- book_2 = Book("The Selfish Gene", "A popular science book", author_1)
- book_3 = Book("The Blind Watchmaker", "The theory of evolutio", author_1) # typo
-
- session.add(author_1)
- session.add(author_2)
- session.add(book_1)
- session.add(book_2)
- session.add(book_3) # or simply session.add_all([author_1, author_2, book_1, book_2, book_3])
-
- # session.flush()
- session.commit() # flushes (issues the statements and sends them to the RDBMS) and commits
-
- book_3.description = "The theory of evolution" # update the object
- book_3 in session # check whether the object is in the session
- # Out: True
-
- session.commit()
-
-
-
-
-
-
Example (cont...)
-
Queries
-
-
- session.query(Book).order_by(Book.id) # returns a Query instance with a .statement attribute
- session.query(Book).order_by(Book.id).all() # returns an object-list
-
- # return all book objects where title == "The Selfish Gene"
- session.query(Book).filter(Book.title == "The Selfish Gene").order_by(Book.id).all()
-
- # using LIKE
- session.query(Book).filter(Book.title.like("The%")).order_by(Book.id).all()
-
- query = session.query(Book).filter(Book.id == 9).order_by(Book.id)
- query.count() # returns 0L
- query.all() # returns an empty list
- query.first() # returns None
- query.one() # raises NoResultFound exception
-
- query = session.query(Book).filter(Book.id == 1).order_by(Book.id)
- book_1 = query.one()
- book_1.description # returns "A popular science book"
- book_1.author.books # returns a list of Book-objects representing all the books from the same author.
-
- # get a list of all Book-instances where the author"s name is "Richard Dawkins"
- session.query(Book).filter(Book.author_id == Author.id).filter(Author.name == "Richard Dawkins").all()
- session.query(Book).join(Author).filter(Author.name == "Richard Dawkins").all()
- session.query(Book).\
- from_statement("SELECT b.* FROM books b, authors a WHERE b.author_id = a.id AND a.name=:name").\
- params(name="Richard Dawkins").all()
-
-