Study Guide 2023+

python

Warning: These notes are partial, ongoing, incomplete, and may contain typos/inaccuracies. (They are kept factually accurate, time permitting.)

They are being united from many disparate notes created in the past and the layout/organization will gradually improve with time!

Please view them on a computer as they are not optimized for mobile (although you can still view them on Mobile along with the Flashcards at your own risk)!

Topics and code examples are lazy-loaded and may require two-clicks from the TOC to correctly calculate the updated x,y coordinates (after rendering). Thanks!

Python: General Concepts

  1. Indentation matters:
    • Is enforced and will complain loudly if you don't indent correctly!
    • Has syntactic and semantic relevance:
      • def is one indentation less than code blocks.
      • The indentation can alter the meaning of a block (for instance, whether code terminates or not).
  2. Dynamically Typed
  3. Python is both Interpreted and Compiled:
    • It's Interpreted Just in Time.
      • So, it's Partially Interpreted.
    • However, it's also Partially Compiled (.pyc files).
  4. Object Oriented:
    • Supports Multiple Inheritance which Java does not.
    • Supports sub-classing without an explicit extends keyword (as in JavaScript and Java).
  5. None is the relevant Null-ish Type and Value:
    • None is Unique (a Singleton).
  6. except is the relevant catch keyword.
    • raise is the relevant throws keyword.
  7. Comparisons:
    • == compares sameness of Value
    • is compares by (strict) Object identity (by same hash of the Memory Address).
  8. self is the relevant this convention (passed by Argument only - it is de facto reserved since the convention is enforced and used through native Python).
  9. with is a convenient and concise shorthand offering the following benefits:
    • Scope (Context).
    • Implicit try-except block.
    • Limited error handling.
    • Variable declaration or alias.
  1. https://docs.python.org/3/library/functions.html#id
  2. https://www.geeksforgeeks.org/with-statement-in-python/

Code samples:

  1. https://github.com/Thoughtscript/_project_euler
  2. https://github.com/Thoughtscript/python-refresh
  3. https://github.com/Thoughtscript/python_api

Python: Data Structures

Numbers

  1. No max int. This makes Python attractive for handling very big numbers.
# https://projecteuler.net/problem=36

import math

if __name__ == "__main__":

    try:

        def is_palindrome(num_str):
            LEN = len(num_str)
            HALF = int(math.floor(len(num_str) / 2))

            for x in range(0, HALF, 1):
                if num_str[x] == num_str[LEN - 1 - x]:
                   continue
                else:
                    #print(num_str + " is not a palindrome")
                    return False
            
            #print(num_str + " is a palindrome")
            return True

        def to_binary(num):
            return format(num, 'b')

        # print(to_binary(585))

        def solve():

            result = []

            for x in range(0, 1000000, 1):
                num_str = str(x)
                A = is_palindrome(num_str)
                binary = to_binary(x)
                B = is_palindrome(binary)

                if A and B:
                    print("Double-base palindrome found: " + num_str)
                    result.append(x)

            print(result)

            sum = 0

            for x in range(0, len(result), 1):
                sum = sum + int(result[x])

            print("Sum found: " + str(sum))
            return sum
       
        solve()
      
    except Exception as ex:

        print("Exception: " + str(ex))

List

  1. Ordered sequence of items.
  2. Does not need to be singly typed (items can be of varying types).
lst = list()
lst.append("example")

if ("test" not in lst):
    print("not in")

if ("example" in lst):
    print("is in")

Tuple

  1. Immutable
  2. Corresponds to the mathematical concept of an ordered N-Tuples.
  3. Ordered.
# 3-tuple
exampleTuple = (1,2,3)

# Access tuples
print(exampleTuple[2])

# Tuples can't be changed - they are immutable
## With throw error if uncommented

### exampleTuple[1] = 44

# Destructuring
(x, y, z) = (9,10,11)
print(x)
print(y)
print(z)
print((x, y, z))

# Comparison - element by element
print(exampleTuple < (x, y, z))
print(exampleTuple > (1000, "", "Hello"))

String

line = "abcdefghijklmnop..."

# indexOf
startIndex = line.find("0.")

# Length of String
endIndex = len(line)

# Slice
line[startIndex:endIndex]

Sets

  1. Share curly brace reserved symbols {,} with Dictionaries but are element-valued only.
  2. Do not guarantee nor preserve order.
  3. Deduplicated.
  4. Corresponds to the mathematical concept of a Set.
thisset = {"apple", "banana", "cherry", "apple"}

Dictionary

  1. Key-Value data structure.
  2. Share curly brace reserved symbols {,} with Sets but are Key-Value.
exampleDictionary = {
    "field": "example",
    "attribute": "another",
    "numerical": 2
}

print(exampleDictionary)

# access value of dict by key
numericalOne = exampleDictionary.get("numerical")
print(numericalOne)

numericalTwo = exampleDictionary["numerical"]
print(numericalTwo)

# set value of dict
exampleDictionary["numerical"] = 4
print(exampleDictionary["numerical"])

# iterating through the dict
## keys
for x in exampleDictionary:
    print(x)

## values
for x in exampleDictionary:
    print(exampleDictionary[x])

for x in exampleDictionary.values():
    print(x)

## keys and values by destructuring
for x, y in exampleDictionary.items():
    print(x, y)

Boolean

  1. Capitalized.
False
True

Code samples:

  1. https://github.com/Thoughtscript/_project_euler/tree/main/_finished
  2. https://github.com/Thoughtscript/python-refresh/tree/master/courses

Python: Comprehensions

Comperehensions provide syntactic sugar for initializing iterables. They use the following succinct Generator Expression syntax:

  1. Element Comprehensions - x for x in range(0, 9)
  2. Key-Value Comprehensions - x : x + 1 for x in range(0, 9)
    • Left of the : specifies the Key and the right determines the resolved Value.

Generator Expressions

Very succinct in terms of written code and memory use. (Consider the extremely verbose initialization of Java nested Arrays with non-default values or static block initializations!)

print([y+y for y in range(0, 10)])
# [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

Consult: https://peps.python.org/pep-0289/

Tuple

Tuple Comprehension isn't supported since Tuples already use Generators (Generator Expressions to be more precise) under the hood.

Review: https://stackoverflow.com/questions/16940293/why-is-there-no-tuple-comprehension-in-python

List

example_list = [x for x in range(0, 9)]
print(example_list) # [0, 1, 2, 3, 4, 5, 6, 7, 8]

Set

example_set = {x for x in range(0, 9)}
print(example_set) # {0, 1, 2, 3, 4, 5, 6, 7, 8}

Dict

example_dict = { x : x + 1 for x in range(0, 9) }
print(example_dict) # {0: 1, 1: 2, 2: 3, 3: 4, 4: 5, 5: 6, 6: 7, 7: 8, 8: 9}
  1. https://stackoverflow.com/questions/16940293/why-is-there-no-tuple-comprehension-in-python
  2. https://peps.python.org/pep-0289/

Python: Object Oriented Design

Classes, Inheritance, and Multiple Inheritance

class Dog:
    breed = "grayhound"
    name = "fido"

class Cat:

    # constructor
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    # instance method
    def meow(self, meow):
        ## string interpolation
        return "{} says meow {}".format(self.name, meow)

# executable code
cat = Cat("Sonata", "black cat")
print(cat.meow("meow")) # Sonata says meow meow

class RoboCat(Cat):

    # constructor with super
    def __init__(self, name, breed, metal):
        super().__init__(name, breed)
        self.metal = metal

# Multiple Inheritance
class CatDog(Cat, Dog):

    # constructor with super
    def __init__(self, name, breed):
        super().__init__(name, breed)

# executable code
catdog = CatDog("CatDog", "cat dog")
print(catdog.meow("meow")) # CatDog says meow meow

Code samples:

  1. https://github.com/Thoughtscript/python-refresh/blob/master/examples/4%20-%20dependency/dependency.py

Python: Modules and Packages

Python scripts and files can be organized or grouped into Modules and Packages.

By default, Modules can be imported (using the import keyword) using a Namespace corresponding to the named directory structure.

A Package can also be defined with a customized Namespace (the Namespace itself for instance) and is typified by the presence of __init__.py files at the root level of the Package.

Importing and Dependency Injection

Given:

+- /example
|   +- /dependencies
|       +- a.py
+- dependency.py
+- main.py

/dependencies/a.py:

class B:
    num = 0

dependency.py:

class Dog:
    breed = "grayhound"
    name = "fido"

# multiple classes in one script
class Cat:

    # constructor
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

    # instance method
    def meow(self, meow):
        ## string interpolation
        return "{} says meow {}".format(self.name, meow)

# executable code
cat = Cat("Sonata", "black cat")
print(cat.meow("meow"))

class RoboCat(Cat):

    # constructor with super
    def __init__(self, name, breed, metal):
        super().__init__(name, breed)
        self.metal = metal

main.py:

import dependency
import dependencies.a as A

if __name__ == '__main__':

    try:
        # dependency injection example
        robo = dependency.RoboCat("cat one", "Egyptian", "chrome")
        print(robo.meow("10101010011"))

        # a second dependency injection example
        B = A.B
        print("A.B " + str(B.num))

    except Exception as ex:

        print('Exception! ' + str(ex))

Review: https://github.com/Thoughtscript/python-refresh/tree/master/examples/4%20-%20dependency

Limitations and Dynamic Importing

Python Modules aren't as flexible as they are in some other langauges/frameworks (dynamic importing using importlib is much more involved than dynamically loading a CommonJS Module in JavaScript):

In Java, Packages have less of an envelope:

Monkey Patching

Replacing a predefined Function or Method with another one (dynamically):

# Given the above imported classes...

def monkey_patch_function(self):
    print("I'm a monkey now!")

dependency.Cat.meow = monkey_patch_function

monkeycat = Cat("Monkey Cat", "money cat")
monkeycat.meow() # I'm a monkey now!

Packages

Consult: https://github.com/Thoughtscript/python_api/blob/main/server/init/__init__.py

  1. https://stackoverflow.com/questions/448271/what-is-init-py-for
  2. https://www.geeksforgeeks.org/python/python-import-module-outside-directory/
  3. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import

Code samples:

  1. https://github.com/Thoughtscript/python-refresh/tree/master/examples/4%20-%20dependency
  2. https://github.com/Thoughtscript/python_api/blob/main/server/init/__init__.py
  3. https://github.com/Thoughtscript/python_api_2023/blob/main/backend/server/init/mlapi.py#L5-L9

Python: Error Handling

None

None is the relevant nil, null, and undefined keyword, concept, and Object.

None is a Singleton - it is uniquely created and one copy exists.

Use the is keyword to check for None.

if x is None:
    # ...

Errors and Exceptions

In Java, an Error specifies an application-terminating event whereas an Exception is something that deviates from some expectation and ought to be handled in most circumstances.

Python Errors refer to syntax-specific concerns. Whereas an Exception is anything thrown (or "raised" via the raise keyword).

try:
    raise NameError('HiThere')

except NameError:
    print('An exception flew by!')

    # rethrow
    raise

https://docs.python.org/3/tutorial/errors.html#raising-exceptions

Exception Handling

Three primary keywords (try, except, and finally) are used to handle a raised Exception:

if __name__ == '__main__':
    try:
        # ...

    except Exception as ex:
        # Executes when error is thrown
        # ...

    else:
        # else can be supplied here
        # even with a subsequent finally clause

    finally:
        # Always executes regardless of thrown error
        # ...

Exception Groupings

Python supports many Exceptions being raised simultaneously without the need for more complex abstraction:

def f():
    # Define a List of Exceptions to pass
    excs = [OSError('error 1'), SystemError('error 2')]
    # raise and throw them all with an ExceptionGroup
    raise ExceptionGroup('there were problems', excs)

# ...

try:
    f()

# Use top-level Exception to catch them in an except clause
except Exception as e:
    print(f'caught {type(e)}: e')

https://docs.python.org/3/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions

  1. https://docs.python.org/3/tutorial/errors.html
  2. https://docs.python.org/3/tutorial/errors.html#raising-exceptions
  3. https://docs.python.org/3/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions

Code samples:

  1. https://github.com/Thoughtscript/_project_euler
  2. https://github.com/Thoughtscript/python-refresh

Python: Truth Values

Falsy Values

None

False

# Any zero number:
0, 0L, 0.0, 0j

# Any empty sequence:
'', (), []

# Any empty mapping:
{}

Truthy Values

All values that aren't Falsy are considered True.

Logical Operators

Python's Logical Operators (or, and, not) Circuit Break (are evaulated Lazily).

https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not

  1. https://docs.python.org/2.4/lib/truth.html
  2. https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not

Python: Pass By Assignment

Pass By Assignment

Other monnikers: Call by Object Reference or Call by Assignment.

Python uses a mix of approaches:

  1. Pass By Value - passes the Value and not the Object Reference as exhibited below:

    def call_by_value(val):
        val *= 2
        print("within call_by_value: " + str(val))
    
    num = 1
    print("before: " + str(num))
    call_by_value(num)
    print("after: " + str(num))
    
    before: 1
    within call_by_value: 2
    after: 1
    

    num is not modified in the outer scope.

  2. Pass By Reference - when the entire Object is passed per the following:

    def call_by_ref(exm_mp):
        exm_mp["a"] = "b"
        print("within call_by_ref: " + str(exm_mp))
    
    exm_mp = {}
    print("before: " + str(exm_mp))
    call_by_ref(exm_mp)
    print("after: " + str(exm_mp))
    
    before: {}
    within call_by_ref: {'a': 'b'}
    after: {'a': 'b'}
    

    exm_mp is modified in the outer scope and retains changes made within the Function.

  3. Pass By Assignment - a species of Pass By Reference that's specific to Python.

    Values can be associated with multiple References (similar to the way Java's String Pool allows the same String Value to be used for multiple String Variables).

    from sys import getrefcount
    
    print("--- Before  assignment ---")
    print(f"References to 1: {getrefcount(1)}")
    print(f"References to 2: {getrefcount(2)}")
    x = 1
    print("--- After   assignment ---")
    print(f"References to 1: {getrefcount(1)}")
    print(f"References to 2: {getrefcount(2)}")
    x = 2
    print("--- After reassignment ---")
    print(f"References to 1: {getrefcount(1)}")
    print(f"References to 2: {getrefcount(2)}")
    print("--- Addresses in Memory ---")
    print(id(x))
    print(id(1))
    print(id(2))
    print("--- Comparisons ---")
    print(id(x) == id(1))
    print(id(x) == id(2))
    print(x is 1)
    print(x is 2)
    print(x == 1)
    print(x == 2)
    

    Notice that the count changes based on how x is set. Also, the high number of References to 1 and 2 even before x References either.

    main.py:21: SyntaxWarning: "is" with a literal. Did you mean "=="?
      print(x is 1)
    main.py:22: SyntaxWarning: "is" with a literal. Did you mean "=="?
      print(x is 2)
    --- Before  assignment ---
    References to 1: 129
    References to 2: 111
    --- After   assignment ---
    References to 1: 130
    References to 2: 111
    --- After reassignment ---
    References to 1: 129
    References to 2: 112
    --- Addresses in Memory ---
    140174349960512
    140174349960480
    140174349960512
    --- Comparisons ---
    False
    True
    False # See Warning above
    True # See Warning above
    False
    True
    

Comparing Objects

Can use id() to find the unique identifier for an Object (the Address of the Object in memory):

num = 1
print(id(num)) # 132506274753480

(Object Reference is also used within is comparison checks.)

Consult: https://www.w3schools.com/python/ref_func_id.asp

Documentation: https://docs.python.org/3/library/functions.html#id

  1. https://realpython.com/python-pass-by-reference/#passing-arguments-in-python
  2. https://www.geeksforgeeks.org/is-python-call-by-reference-or-call-by-value/
  3. https://www.w3schools.com/python/ref_func_id.asp
  4. https://docs.python.org/3/library/functions.html#id

Python: Asynchronous Libraries

asyncio

Uses the familiar keywords and syntax of async, await in JavaScript:

import asyncio

async def count(run_var):
    print(run_var + "One")
    await asyncio.sleep(1)
    print(run_var + "Two")

# Must run and call async functions with await
async def main_method():
    await asyncio.gather(count("a"), count("b"), count("c"))

if __name__ == "__main__":
    import time
    s = time.perf_counter()
    # Use asyncio runner
    # https://docs.python.org/3/library/asyncio-runner.html
    asyncio.run(main_method())
    elapsed = time.perf_counter() - s
    print(f"{__file__} executed in {elapsed:0.2f} seconds.")
aOne
bOne
cOne
aTwo
bTwo
cTwo
  1. https://docs.python.org/3/library/asyncio.html
  2. https://docs.python.org/3/library/asyncio-runner.html

Python: Concurrency and Threading

AttributeError

Be forewarned that multiprocessing.Pool requires an imported function as its target (something not mentioned in the official documentation).

  1. https://stackoverflow.com/questions/41385708/multiprocessing-example-giving-attributeerror
  2. https://bugs.python.org/issue25053
  3. https://github.com/python/cpython/issues/69240
  1. https://docs.python.org/3/library/threading.html
  2. https://docs.python.org/3/library/multiprocessing.html
  3. https://stackoverflow.com/questions/41385708/multiprocessing-example-giving-attributeerror
  4. https://bugs.python.org/issue25053
  5. https://github.com/python/cpython/issues/69240

Code samples:

  1. https://github.com/Thoughtscript/python_processing_2024

Python: Techniques

Commenting

'''
I'm a multiline docstring!

I can be accessed via introspection: __doc___
'''

# I'm a single line comment

Review: https://docs.python.org/3/glossary.html#term-docstring

Float and Floor Division

'''
Float Division 
- Results in a float 
'''
5 / 2 # 2.5

'''
Floor Division 
- Results in an integer rounded down
'''
5 // 2 # 2

// behaves like Integer or Math.round() division in Java / JavaScript.

Consult also: https://docs.python.org/3/library/functions.html#divmod

args and kwargs

Most similar to Spread Operator ( ...) in JavaScript.

Provides Function Parameter and Argument flexiblity slightly akin to Java's Generic and Go's Empty Interface (in that supplied Arguments can vary beyond that which is singularly or precisely stipulated).

# Any number can be passed - variable length
# Can also just pass a List
def my_func_a(*args_example):
    for arg in args_example:
        print(arg)

# Use
my_func_a('a', 'b', 'c')

# Use in tandem with other required arguments
def my_func_b(arg, *args_example):
    for argv in args_example:
        print(argv)

# Use
my_func_b('arg', 'a', 'b', 'c', 'd')

# Use kwargs to pass in a varying number of arguments
# These are key-value pairs
def my_func_c(**kwargs):
    for key, value in kwargs.items():
        print("Key: {0} Value: {1}".format(key, value))

# Use
my_func_c(a='a', b='b', c='c', d='d')

Consider: https://nodejs.org/api/process.html#processargv process.argv in Node.

Pickling and Unpickling

Akin to Java's transient keyword which uses a customized hashing mechanism to divide a value into files and persist them.

The Pickle Module saves an Object or Value as a String within a File.

Unpickling involves deserializing that value back into an in-memory Python Object or Value.

Pass

pass can be supplied in leiu of code block:

def example_a():
    pass

# note the following isn't syntactically valid
def example_b():
    # nothing here

REST API

Using Flask and backing SQL database:

from init import get_app
from flask import request
from domain import scan_examples, get_example, delete_example, create_example, update_example

"""
DB endpoints.
"""

app = get_app()

@app.route("/api/db/example", methods=['POST'])
def postExample():
    name = request.form.get('name', '')
    result = create_example(name)

    return [str(result)]

@app.route("/api/db/examples", methods=['GET'])
def scanExamples():
    results = scan_examples()
    response = []

    for x in results:
        response.append(str(x))

    return response

@app.route("/api/db/example/<id>", methods=['PUT'])
def updateExample(id):
    name = request.form.get('name', '')
    result = update_example(id, name)

    return [str(result)]

@app.route("/api/db/example/<id>", methods=['GET'])
def getExample(id):
    result = get_example(id)

    return [str(result)]

@app.route("/api/db/example/<id>", methods=['DELETE'])
def deleteExample(id):
    result = delete_example(id)

    return [str(result)]
from init import db

"""
Domain.
"""

class Example(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=False)

    def __init__(self, name):
        self.name = name

    # Must be valid JSON serializable string
    def __repr__(self):
        return '{ id: %s, name: %r }' %(self.id, self.name)

"""
Queries.
"""

def scan_examples():
    return Example.query.all()

def get_example(id):
    return Example.query.get(id)

def delete_example(id):
    example = Example.query.get(id)
    db.session.delete(example)
    db.session.commit()
    return example

def update_example(id, name):
    example = Example.query.get(id)
    example.name = name
    db.session.commit()

    result = Example.query.get(id)
    return result

def create_example(name):
    example = Example(name)
    db.session.add(example)
    db.session.commit()

    return example

def prepopulate():
    db.drop_all()

    db.create_all()

    example_a = Example('example_a')
    db.session.add(example_a)

    example_b = Example('example_b')
    db.session.add(example_b)

    example_c = Example('example_c')
    db.session.add(example_c)

    example_d = Example('example_d')
    db.session.add(example_d)

    db.session.commit()

Review: https://github.com/Thoughtscript/python_api/tree/main/server

Reverse Proxy

It's standard practice to put something like an nginx server out in front as a Reverse-Proxy for Web Applications deployed using Gunicorn or uvicorn:

This is akin to Tomcat serving as a Web Container for a specific Servlet or Spring app.

NAN

Check if x is a Number:

import numbers

if isinstance(x, numbers.Number):
    # ...

Numpy

N-Step Markov Transition Probability Matrix

Encountered when computing the liklihood of some weather pattern occuring (apparently).

  1. Take the initial Matrix matrix and apply Matrix Multiplication n times (where n is the number of steps to take) from a starting pattern to an ending pattern.
    • result = matrixⁿ
    • Or, result = matrix₁ × ... × matrixₙ
  2. The resultant value is found at index: result[starting][ending] of the Matrix computed above.

Example from Codewars:

import numpy as np

# https://numpy.org/doc/2.1/reference/generated/numpy.matmul.html
## This was extremely helpful: https://www.geeksforgeeks.org/transition-probability-matrix/
def weather_prediction(days, weather_today, final_weather, P):
    day = 1
    mat = np.array(P)
    
    while (day < days):
        mat = np.matmul(mat, np.array(P))
        day += 1
    
    return mat[weather_today][final_weather]
  1. https://docs.python.org/3/glossary.html#term-docstring
  2. https://docs.python.org/3/library/functions.html#divmod
  3. https://nodejs.org/api/process.html#processargv
  4. https://numpy.org/doc/2.1/reference/generated/numpy.matmul.html
  5. https://www.geeksforgeeks.org/transition-probability-matrix/

Code samples:

  1. https://github.com/Thoughtscript/python_api/tree/main/server
  2. https://www.codewars.com/kata/602d1d769a1edc000cf59e4c