## Unit 1 (Review and Programming Practice)

### Section 1

• Why decimal numbers are natural for humans, and why binary numbers are more natural for computers.
• What is a 0 and a 1 in computer hardware.
• Properties of, and operations with binary numbers.
• The Carry, and the Ripple Effect.
• Review: various types of variables, such as integer (int), real number (float), text string (str), tuple (tuple), list (list), dictionary (dict) etc.
• Review: checking types of variables using the built-in Boolean function isinstance().
• Review: generating random integers using the function randint() from the random library.
• Generating random binary numbers.
• Review: integer division operator // and the modulo operator %.
• Converting decimal numbers to binary and vice versa.
• Addition of decimal and binary integers.

### Section 2

• Properties of, and operations with hexadecimal numbers.
• Main applications of hex numbers: represent locations (addresses) in computer memory, MAC (hardware) addresses, and color codes.
• Review: checking whether a given item is present in a list.
• Using the ASCII table to generate hex numbers.
• Converting decimal numbers to hexadecimal and vice versa.
• Hex numbers may contain both lowercase or uppercase characters.
• Special relationship between hexadecimal and binary numbers.
• Converting hex numbers to binary one digit at a time.
• Converting binary numbers to hexadecimal in four-digit chunks.
• Review: using assertions and exceptions to protect functions against flawed input data.
• Sometimes, sanity checks may take more lines than the actual function body.

### Section 3

• Transistor count, Moore’s Law, and CPU clock speed.
• Exploring transistors and logic gates.
• Using Python to simulate the seven basic types of logic gates: AND, OR, NOT, NAND, NOR, XOR, and XNOR.
• Practicing safe programming with assertions and exceptions.

### Section 4

• Combining logic gates and simulating simple logic circuits in Python.
• Simulating the XOR gate using the AND, OR and NAND gates, as well as using the AND, OR and NOT gates.
• Simulating the Half Adder using the XOR and AND gates.
• Simulating the Two-Bit, Four-Bit and Eight-Bit Adders as arrays of Full Adders.
• Simulating the Full-Adder using the XOR, AND and OR gates.

### Section 5

• Various uses of the underscore character _ in Python.
• Role of the so-called “shift” in multiplication.
• Performing multiplication with just addition and shift.
• Simulating the Four-Bit Multiplier using the Eight-Bit Adder.
• Simulating the Eight-Bit Multiplier using the Sixteen-Bit Adder.
• Using the bitwise shift operators <<, >>, <<= and >>= in Python.

## Unit 2 (Object-Oriented Programming I)

### Section 6

• Brief history of computers and programming languages.
• Importance of using well-designed data structures.
• How did object-oriented programming evolve from procedural programming.
• Main differences between procedural and object-oriented programming.
• Classes consist of data (attributes) and functions (methods).
• An object is an instance of a class.
• A class can have many different instances.
• Instances of a class do not share data.
• Methods are defined on the level of a class but used on the level of an instance.
• All data types in Python are classes.
• The so-called “magic methods” in Python and how they work.
• The bitwise operators & (AND) and | (OR).

### Section 7

• Defining new classes using the keyword class.
• Using the constructor __init__() to define class attributes and initialize their values.
• The constructor as well as all class methods must have a mandatory first parameter self.
• All attributes and methods of a class must be accessed through the prefix self.
• Adding new methods and new attributes to a class.
• Instantiating a class.
• It is a good programming practice in OOP to always access class attributes via class methods (not directly).
• Using the Sympy and other Math libraries.
• ‘self’ is not a Python keyword.
• Creating two training classes: Vocabulary and Number.

### Section 8

• Defining new classes with the keyword class.
• Using the constructor __init__() to define class attributes and initialize their values.
• Adding new methods and new attributes to a class.
• Instantiating a class.
• Using file operations and working with regular expressions.
• Not every class needs to have attributes.
• A class without attributes does not need a constructor.
• Creating two new classes: Converter and DataMiner.

### Section 9

• Implementing class Line to represent lines in the XY plane:
• Attributes: startx, starty, angle, length, lw (line width), color, ls (line style).
• Methods: __init__(), get_endpoint(), set_endpoint(), plot(), get_length(), get_area().
• Making data structures flexible and open to future development, even though this could mean that they require more memory.
• Premature optimization is the root of all evil (or at least most of it) in programming.
• Matplotlib has four different line styles, and how to define them.
• Objects can be stored in lists like any other variables.

### Section 10

• Using the class Line as a basis to implement class Turtle, a simplified version of the well known Python Turtle Graphics:
• Attributes: posx, posy, angle, linewidth, linecolor, linestyle, drawing, lines.
• Methods: __init__(), go(), left(), right(), penup(), pendown(), goto(), back(), set_angle(), show(self), width(), color(), style(), get_length(), get_area().
• Practicing working with exceptions.
• Exploring additional useful functions of Matplotlib.

## Unit 3 (Object-Oriented Programming II)

### Section 11

• Static methods and when they can be useful.
• How to derive a subclass from a superclass.
• When a new subclass should be created, as opposed to modifying the original class.
• If the superclass has attributes, then the constructor of the subclass must call the constructor of the superclass.
• Using the function super() to access the superclass.
• Subclasses can add new attributes and methods, as well as override methods.
• Importance of a well-designed class hierarchy.
• What is polymorphism and when it can be useful.
• What are abstract methods and abstract classes, and when they can be useful.
• Using the abc (Abstract Base Classes) module.
• Using ternary conditional expressions.

### Section 12

• Programming practice: Upgrading the Graphics Editor from Python Fundamentals to an object-oriented design (Phase 1).
• Designing a base class Shape with 6 attributes and 20 methods.
• Meaning of single and double underscores in the names of class attributes and methods.
• Review: PEP 8 – Style Guide for Python Code, copying lists via slicing, list comprehension, shallow and deep copying, obtaining memory addresses of Python objects, accessing class attributes via dedicated class methods.

### Section 13

• Programming practice: Upgrading the Graphics Editor from Python Fundamentals to an object-oriented design (Phase 2).
• Designing and implementing a hierarchy of classes to represent 10 basic geometric shapes.
• Practicing class creation and inheritance.

### Section 14

• Programming practice: Upgrading the Graphics Editor from Python Fundamentals to an object-oriented design (Phase 3).
• Designing and implementing class Figure to represent figures consisting of one or more basic shapes.
• Practicing class creation and inheritance.

### Section 15

• Programming practice: Upgrading the Graphics Editor from Python Fundamentals to an object-oriented design (Phase 4).
• Designing and implementing three different classes SimpleDrawing, SmartDrawing, and TechDrawing based on three different use cases.
• Analyzing an example of bad object-oriented programming.
• In OOP, classes should always work with their own attributes, not with the attributes of other classes.
• Multiple inheritance, its applications, and its limitations.
• Multiple inheritance vs. standard inheritance.
• The Diamond Problem and when it represents a real problem.
• Mixin classes.

## Unit 4 (Selected Advanced Techniques)

### Section 16

• Recursion and its applications.
• Flattening lists.
• Polish (prefix) notation.
• Interactive input.
• Functions eval() and exec().
• Anonymous (lambda) functions.

### Section 17

• Various types of tree data structures.
• Using recursion to work with binary trees.
• Iterables and iterators, functions iter() and next().
• Built-in functions any() and all().
• Defining custom iterators from scratch.
• Making iterators via generator functions and generator expressions.
• Applying maps and filters to data, functions map() and filter().
• Using the function reduce() for cumulative operations with iterables.

### Section 18

• Using metasyntactic variables.
• Working with names and namespaces.
• Creating modules and packages.
• Using built-in decorators.
• Creating custom decorators.
• Writing decorators with parameters.
• Queuing (chaining) decorators.
• Decorating class methods.
• Making Python classes callable.
• Working with JSON and XML data.

### Section 19

• Overview of the Pandas and Seaborn libraries.
• Structure of Pandas DataFrames.
• Entering data into a DataFrame.
• Creating scatter plots, regression plots, and residual plots with Seaborn.
• Using the residual plot to justify the linear regression model.
• Making simple predictions.

### Section 20

• Using Pandas to read files from the hard disk or from a URL.
• Obtaining the number of rows and number of columns of a DataFrame.
• Obtaining the list of all column names of a DataFrame.
• Displaying the beginning and the end of a DataFrame.
• Other operations with DataFrames.
• Cleaning data.