Python: General Concepts
- Indentation matters:
- Is enforced and will complain loudly if you don't indent correctly!
- Has syntactic and semantic relevance:
defis one indentation less than code blocks.- The indentation can alter the meaning of a block (for instance, whether code terminates or not).
- Dynamically Typed
- Python is both Interpreted and Compiled:
- It's Interpreted Just in Time.
- So, it's Partially Interpreted.
- However, it's also Partially Compiled (
.pycfiles).
- It's Interpreted Just in Time.
- Object Oriented:
- Supports Multiple Inheritance which Java does not.
- Supports sub-classing without an explicit
extendskeyword (as in JavaScript and Java).
Noneis the relevantNull-ish Type and Value:Noneis Unique (a Singleton).
exceptis the relevantcatchkeyword.raiseis the relevantthrowskeyword.
- Comparisons:
==compares sameness of Valueiscompares by (strict) Object identity (by same hash of the Memory Address).
selfis the relevantthisconvention (passed by Argument only - it is de facto reserved since the convention is enforced and used through native Python).withis a convenient and concise shorthand offering the following benefits:- Scope (Context).
- Implicit
try-exceptblock. - Limited error handling.
- Variable declaration or alias.
Resources and Links
- https://docs.python.org/3/library/functions.html#id
- https://www.geeksforgeeks.org/with-statement-in-python/
Code samples:
Python: Data Structures
Numbers
- 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
- Ordered sequence of items.
- 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
- Immutable
- Corresponds to the mathematical concept of an ordered N-Tuples.
- 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
- Share curly brace reserved symbols
{,}with Dictionaries but are element-valued only. - Do not guarantee nor preserve order.
- Deduplicated.
- Corresponds to the mathematical concept of a Set.
thisset = {"apple", "banana", "cherry", "apple"}
Dictionary
- Key-Value data structure.
- 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
- Capitalized.
False
True
Resources and Links
Code samples:
Python: Comprehensions
Comperehensions provide syntactic sugar for initializing iterables. They use the following succinct Generator Expression syntax:
- Element Comprehensions -
x for x in range(0, 9) - 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.
- Left of the
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}
Resources and Links
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
Resources and Links
Code samples:
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):
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
- https://www.geeksforgeeks.org/python/python-import-module-outside-directory/
- https://github.com/Thoughtscript/python_api_2023/blob/9678c7f9a0d4c15af47afa5400dbf176d8f174e4/backend/server/init/mlapi.py#L5-L9
In Java, Packages have less of an envelope:
- They are invisible file-wise (expressed inline but not by adding something like a specialized script file:
__init__.py). - No wonky dynamic loading scenarios from within the same Application Context using the same degree of compilation (compiled Classes).
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
Resources and Links
- https://stackoverflow.com/questions/448271/what-is-init-py-for
- https://www.geeksforgeeks.org/python/python-import-module-outside-directory/
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import
Code samples:
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
iskeyword to check forNone.
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
Resources and Links
- https://docs.python.org/3/tutorial/errors.html
- https://docs.python.org/3/tutorial/errors.html#raising-exceptions
- https://docs.python.org/3/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions
Code samples:
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
References and Links
Python: Pass By Assignment
Pass By Assignment
Other monnikers: Call by Object Reference or Call by Assignment.
Python uses a mix of approaches:
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: 1numis not modified in the outer scope.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_mpis modified in the outer scope and retains changes made within the Function.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
xis set. Also, the high number of References to1and2even beforexReferences 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.)
Documentation: https://docs.python.org/3/library/functions.html#id
References and Links
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
Resources an Links
Python: Concurrency and Threading
AttributeError
Be forewarned that
multiprocessing.Poolrequires an imported function as itstarget(something not mentioned in the official documentation).
- https://stackoverflow.com/questions/41385708/multiprocessing-example-giving-attributeerror
- https://bugs.python.org/issue25053
- https://github.com/python/cpython/issues/69240
Resources and Links
- https://docs.python.org/3/library/threading.html
- https://docs.python.org/3/library/multiprocessing.html
- https://stackoverflow.com/questions/41385708/multiprocessing-example-giving-attributeerror
- https://bugs.python.org/issue25053
- https://github.com/python/cpython/issues/69240
Code samples:
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 likeIntegerorMath.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.argvin 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 allows the Web Application (say built in Flask) to be assigned non-
rootaccess (LinuxUSER,chown) which necessitates thatportsbe bound to something other than8000or8080. nginxis configured to receive inbound Requests at8000or8080and is then internally routed to the specified Flask application.
This is akin to
Tomcatserving 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).
- Take the initial Matrix
matrixand apply Matrix Multiplicationntimes (wherenis the number of steps to take) from astartingpattern to anendingpattern.result = matrixⁿ- Or,
result = matrix₁ × ... × matrixₙ
- 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]
Resources and Links
- https://docs.python.org/3/glossary.html#term-docstring
- https://docs.python.org/3/library/functions.html#divmod
- https://nodejs.org/api/process.html#processargv
- https://numpy.org/doc/2.1/reference/generated/numpy.matmul.html
- https://www.geeksforgeeks.org/transition-probability-matrix/
Code samples: