Skip to content

Instantly share code, notes, and snippets.

@jjdonson
Last active August 13, 2023 14:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jjdonson/ba899749ec416b30893bdfad4fe7ce2f to your computer and use it in GitHub Desktop.
Save jjdonson/ba899749ec416b30893bdfad4fe7ce2f to your computer and use it in GitHub Desktop.
python3 notebook: oop

Agnostic Spectra

  • Code Pipelines

    • Stack Layers: infra + storage + networks + systems + clusters + services + platforms + languages + uis
    • Environments: local => dev => qa => staging => prod => fed
    • Workflows -< Stages --< Tasks --< Tests + Docs >-- Demos
    • Tools + APIs
    • Scenarios --E
    • Registries
  • Edu Spectra

    • Curriculum
    • Instruction
    • Assessments
  • Major Axes

    • Programming Languages
    • Template Engines
    • Operating Systems
    • Data Types
    • Data Engines
    • Data Sets + Structures
    • Security
    • Human Languages
    • Workflows
    • Time
    • Space
    • Resources => Supply + Demand
    • Cloud Vendors
    • Testing => Diagrams => Docs => Code => Demos => Playgrounds
    • Doc Formats: gdoc => github pages => notebooks => playgrounds
    • Logical + Physical Web App Architectures
  • Minor-Axes

    • Tool Versions + Releases
  • logical : physical


  • tests

  • logs

  • templates

  • diagrams

    • site map
    • workflows
  • gh pages tab ideas

    • py3 • bash • golang • scala • elixir

Python3 Object Oriented Programming

  • Converting great books into a set of evolving test-driven and collaborative notebook lessons and labs.
    • Engineer: Jeremy Donson

Book Metadata

  • Title: Python 3 Object-oriented Programming, 2nd Edition
  • Author: Dusty Phillips
  • Year: 2015
  • Publisher: Packt Open Source

Table of Doc Contents


Lesson 1


Lab 1

  • To deduce emerging-current-legacy python3 versions...

  • To check python version from bash: $ python3 --version

  • To run a python script from bash: $ python3 py3-script.py

  • To run a python script from bash, ending up in python3 interactive terminal: $ python3 -i python3-script.py

  • To learn about which platform python3 is running on: $ python3 -m platform

  • To install python3 modules from bash: $ python3 -m pip install pyspark

  • To load python3 module: import pyspark

  • Script File Name: class-point-min.py

    • Script File Version: 0.1.0
# define named empty class
class Point:
  pass

# create object from class
p1 = Point()
p2 = Point()

# print object
print(p1)
print(p2)

# Was the hardware memory address mismatch expected ??  Why ??

# Add attributes to those objects.
p1.x = 3; p1.y = 2
p2.x = 1; p2.y = 2

print(p1.x, p1.y)
print(p2.x, p2.y)

# Several functions support testing.
def defineOutputDataStructure():
def matchExpectedStructure():
def matchExpectedValues():
def getResult():
def formattedOutput():

points_dict = { 'p1': [p1.x, p1.y], 'p2': [p2.x, p2.y] }

output_data_structure = { 'p1': [p1.x, p1.y], 'p2': [p2.x, p2.y] }

expected_output = { 'p1': [3,2], 'p2': [1,2] }

assert 

Lesson 2

  • How do we create a virtual environment for python development?

  • How does a python 3 class method differ from a standard python 3 function?

  • How do exceptions differ from errors?

  • Terminology

    • Test Framework
    • Test Runs
    • Test Outcomes
    • Test Results

Lab 2

  • Incorrect:

  • filename: point-class-min.py

    • version: 0.2.0
class Point:
  def reset():
    pass

p = Point()

p.reset()

ERROR returned....
  • Correct:

  • filename: point-class-min.py

    • version: 0.2.1
class Point:
  def reset(self):
    self.x = 0
    self.y = 0

p = Point()
p.reset()
print(p.x,p.y)
  • Missing references to self will be reported as missing method args.

Lesson 3


Lab 3

# import native math module
import math

# define named class
class Point:

# create object from class
p1 = Point()
p2 = Point()

# print object
print(p1)
print(p2)

Lesson 4

  • Automate comparison of the output from two separate objects from same class with testing.

Lab 4

quadrants = [ 1, 2, 3, 4 ]
quadrant_default = 1

dimensions = [ 2, 3, 4, 5, 6 ]
dimensions_default = 2

x_axis_units = pixels
y_axis_units = pixels

layers = [ 1, 2, 3, 4 ]

resolution = 72*2 ppi

precision : scale


References

  • Publisher Book Web Page
  • Book Github Repo

temp code

pdf book metadata

import datetime

# store next available id for all new notes
last_id = 0

class Note:
  ''' Represent note in the notebook.
      Match against a string in text content searches.
      Store tags for each note. '''

  def __init__(self, memo, tags=''):
    ''' Initialize a note with memo and optional space-separated tags.
        Automatically set note creation date and unique note id. '''
    self.memo = memo
    self.tags = tags
    self.creation_date = datetime.date.today()
    global last_id
    last_id += 1
    self.id = last_id
    
  def match(self, filter):
    ''' Determine if this note matches filter text. Return True or False.
        Search is case-sensitive, and matches text and tags.
    Return filter in self.memo or filter in self.tags.
  • Iteration I Testing
    • Focused on testing Notes objects.
    • Consider ownership of notebook OR notes?
>>> from notebook import Note
>>> n1 = Note("hello 1")
>>> n2 = Note("hello 2")
>>> n1.id # 1
>>> n2.id # 2
>>> n1.match('hello') # True
>>> n1.match('bollo') # False

  • Iteration II
class Notebook:
  ''' Represent a collection of notes that can be tagged, modified and searched. '''

  def __init__(self):
    ''' Initialize a notebook with an empty list. '''
    self.notes = []

  def new_note(self, memo, tags=''):
    ''' Create a new note and add it to the list. '''
    self.notes.append(Note(memo,tags))

  def modify_memo(self, note_id, memo):
    ''' Find the note with the given id and change its memo to value provided. '''
    for note in self.notes:
      if note.id == note_id:
        note.memo = memo
        break

  def modify_tags(self, note_id, tags):
    ''' Find the note with the given id and change its tags to value provided. '''
    for note in self.notes:
      if note.id == note_id:
        note.tags = tags
        break

  def search(self, filter):
    ''' Find all notes that match the given filter '''
    return [ note for note in self.notes if note.match(filter) ]

  • Iteration II Testing
>>> from notebook import Note, Notebook
>>> n = Notebook()
>>> n                          # ??
>>> n.new_note('hello 1')
>>> n.new_note('hello 2')
>>> n.new_note('hello 3')
>>> n.new_note('hello world')
>>> n                          # ??
>>> n.notes                    # object info
>>> n.notes[0].id              # 1
>>> n.notes[1].id              # 2
>>> n.notes[2].id              # 3
>>> n.notes[0].memo            # 'hello 1'
>>> n.search('hello')
>>> n.search('world')
>>> n.modify_menu(4, 'howdy globe')
  • Iteration III Test-Driven Code Enhancements
def _find_note(self, note_id):
  ''' Locate the note with provided given id. '''
  for note in self.notes:
    if note.id == note_id:
      return note
  return None

def modify_memo(self, note_id, memo):
  ''' Find the note with the given id and change its memo to the given value. '''
  self._find_note(note_id).memo = memo

def modify_tags(self, note_id, tags):
  ''' Find the note with the given id and change its tags to value provided. '''
  self._find_note(note_id).tags = tags

  • Enhancement Support

    • start with procedural code => dfd => uml
    • test complete input domain
    • test for recognized inputs
    • basis for test output expectations
    • comprehending error messages
    • debugging
    • testing sufficiency
    • diff patches
    • pytest + tox
  • Iteration III Menu Interface Code: menu-interface.py

import sys

from notebook import Notebook, Note

class Menu:
  ''' Display menu of choices and respond to choices when run. '''
  
  def __init__(self):
    self.notebook = Notebook()
    self.choices = {
      "1": self.show_notes,
      "2": self.search_notes,
      "3": self.Add_note,
      "4": self.modify_note,
      "5": self.quit
      }

  def display_menu(self):
    print("""
Notebook App Menu

1. Show All Notes
2. Search Notes
3. Add Note
4. Modify Note
5. Quit
```)

  def run(self):
    ''' Display menu and respond to choices. '''
    while True:
      self.display_menu()
      choice = input('Enter an option: ')
      action = self.choices.get(choice)
      if action:
        action()
      else:
        print("{0} is an invalid menu choice".format(choice))

  def show_notes(self, notes=None):
    if not notes:
      notes = self.notebook.notes
    for note in notes:
      print("{0}: {1}\n{2}".format(note.id, note.tags, note.memo))

  def search_notes(self):
    filter = input('Search For: ')
    notes = self.notebook.search(filter)
    self.show_notes(notes)

  def add_note(self):
    memo = input('Enter a memo: ')
    self.notebook.new_note(memo)
    print('Your new note has been added.')

  def modify_note(self):
    id = input('Enter a note id: ')
    memo = input('Enter a memo: ')
    tags = input('Enter tags: ')
    if memo:
      self.notebook.modify_memo(id, memo)
    if tags:
      self.notebook.modify_tags(id, tags)

  def quit(self):
    print('Thank you for using your note book today.')
    sys.exit(0)

if __name__ == "__main__":
  Menu().run()

  • We test menu interface code and discover 2 bugs:

    • 1: Notebook crashes when non-existent note ids are input by user.
    • 2: Even with correct id, menu crashes where integer is expected since it passes string.
  • Bug Fix #1: Change two modify methods to check whether _find_note returned a note or not.

def modify_memo(self, note_id, memo):
  ''' Find the note with given id, and change its memo to the value provided. '''
  note = self._find_note(note_id)
  if note:
    note.memo = memo
    return True
  return False

def modify_tags(self, note_id, tags):
  ''' Find the note with given id, and change its tags to the value provided. '''
  note = self._find_note(note_id)
  if note:
    note.tags = tags
    return True
  return False

  • Why not put this in the _find_note method?
  • Other DRY options?
  • Do we want case-sensitive searches?
  • We will soon further enhance this method by raising an exception.

  • Bug Fix #2: Convert both note.id and note_id to strings to support non-numeric entries.
def _find_note(self, note_id):
  ''' Locate the note with provided given id. '''
  for note in self.notes:
    if str(note.id) == str(note_id):
      return note
  return None


  • Exercises

    • Create single module with 1:many association built in. - Suggestions: vols --< dirs --< files --< lines --< chars
    • Begin testing with min methods.
    • See how that 1:∞ association maps to ERD keys.
    • Identify back end tests to support all possible interface logic.
    • Top-down vs bottom-up design.
  • Code UML Diagram => [load order + deps] + [execution + deps]

    • Command Pattern
  • Code Refactoring Target:

- [app-name]
  - domains --< urls
  - pages --E metadata + content + tags
  - url-pages-registry.py
  - url-pages-tests.py
  - url-aliases-registry.py
  - url-aliases-tests.py

  - menu-interface.py => menu interface
  - cli-tool.py => cli interface
  - web-app.py => web interface

  • Code Sample Name
  • Code Sample Versions: Step Rewinds + Replays
  • Diagrams
  • Anti-patterns
  • Iterations + Diffs
  • Code Templates
  • Tests --E
  • REST Patterns
  • Docs
  • Demos

Chapter 3: Inheritance

  • I: basic inheritance

  • Minimal example: By inheriting directly from an object, this subclass needs no parent class refs.

class MySubClass(object):
  pass
  • Working example: Contact Manager (page 60) Supporting Varied Contact Types
class Contact:
  ''' Each Contact Type gets its own methods.
      CLASS VARIABLE goes into each object instantiated from this class. '''
  all_contacts = [] 
  
  def __init__(self, name, email):
    self.name = name
    self.email = email
    Contact.all_contacts.append.(self)

class Supplier(Contact):
  def order(self, order):
    print("In a real apps, then we would send '{}' order to '{}'.".format(order, self.name)

>>> c = Contact('John Contact Goop', 'goop@gmail.com')
>>> s = Supplier('Jill Supplier Bloof', 'bloof@gmail.com')
>>> print(c.name, c.email, s.name, s.email)
>>> c.all_contacts
>>> c.order('I need pliers.'). # Error
>>> s.order('I also need pliers.') # Real app would send message to proper target.

  • II: Inheriting from built-ins

  • The list can be generated from another (parent) class...

class ContactList(list):
  def search(self, name):
    ''' Return all contacts containing the search value in their name. '''
    matching_contacts = []
    for contact in self:
      if name in contact.name:
        matching_contacts.append(contact)
      return matching_contacts

class Contact:
-  all_contacts = [] 
+  all_contact = ContactList()

# testing
>>> [] == list()                            # True
>>> isinstance([], object).                 # True

>>> c1 = Contact('john 1','j1@example.net')
>>> c2 = Contact('john 2','j2@example.net')
>>> c3 = Contact('joe 3','joe3@example.net')
>>> [ c.name for c in Contact.all_contacts.search('john') ]# Is case-sensitive config toggle desirable?

  • Second extend builtin: dict
class LongNameDict(dict):
  def longest_key(self):
    longest = None
    for key in self:
      if not longest or len(key) > len(longest):
        longest = key
    return longest

>>> longkeys = LongNameDict()
>>> longkeys['hello'] = 1
>>> longkeys['longest yet'] = 5
>>> longkeys['hello2'] = 'world'
>>> longkeys.longest_key() # 'longest yet'
  • Most builtins (object, list, set, dict, file, str) can be extended.

class Friend(Contact):
  def __init_(self, name, email, phone):
    super().__init__(name, email)
    self.phone = phone
  • III: Multiple inheritance

  • Simplest and most useful form of multiple inheritance: mixin

    • Mixins are superclasses designed to be inherited and extended.
    • A fine example from std lib: smtplib
  • Contact types have multiple common communication methods, like phone calls or email sends.

class MailSender:
  def send_mail(self, message):
    print('Sending mail to ' + self.email)
    # Add email logic here...
  • Now we can combine two+ parent classes...
class EmailableContact(Contact, MailSender):
  pass

>>> e = EmailableContact('John Smith', 'jsmith@example.net')
>>> Contact.all_contacts
>>> e.send_mail("Hello, test email here!")
  • Alternatives to mixins

  • Had we added send_mail to the subclass, we would still need to dupe it for other subclasses.

  • Create standalone function for sending email.

  • Explored a few ways of using composition rather than inheritance.

  • Monkey-patch Contact class.

  • inheritance : composition as 'is a' : 'has a'

class AddressHolder:
  def __init__(self, street, city, state, code):
    self.street = street
    self.city = city
    self.state = state
    self.code = code

  • The ⬦ Diamond Problem ⬦

    • Method load sequence: __mro__


  • IV: Polymorphism and duck-typing

Lab Plan Structure

  • Lab Components

  • Lab Roles

  • Lab Workflows

  • Lab Setup

Survey Python3 Course Syllabi

Testing

  • Simplest Code Samples For Testing

    • Bash Statement With Bash Calls: $ echo 'Hello Bash Statement'; uname -a; bash --version

    • Bash Statement With Python3 Calls: $ python3 -m platform; python3 'print(__version__)'

    • Bash Script With Bash Statements: hello-bash-script-00-v1.sh

#!/bin/bash
bash
echo 'Hello Simplest Bash Script!'
exit 0
  • Bash Script Execution + Testing
$ mkdir ~/test-temp-dir/; cd $_
$ SIMPLEST_BASH_SCRIPT_OUTPUT_EXPECTED='Hello Simplest Bash Script!'
$ ./hello-bash-script-00-v1.sh
$ chmod 777 hello-bash-script_00.sh
$ ./hello-bash-script-00-v1.sh
$ SIMPLEST_BASH_SCRIPT_OUTPUT_ACTUAL=$( ./hello-bash-script-00-v1.sh )
$ [[ "${SIMPLEST_BASH_SCRIPT_OUTPUT_ACTUAL} == ${SIMPLEST_BASH_SCRIPT_OUTPUT_EXPECTED} ]] || echo 'no match' && exit 1

  • Bash Script With Python3 Statements
#!/usr/bin/env python
import sys

  • Bash Script Template:
#!/bin/bash

# Hello Bash Script 00

BASH_SCRIPT_FILE_NAME_00='hello-bash-script_00.sh'
BASH_SCRIPT_FILE_CONTENTS_00="echo 'Hello Bash Script'"
echo "" > $BASH_SCRIPT_FILE_NAME
chmod 777 hello-bash-script.sh
./hello-bash-script.sh
>>> print('Hello Python')
  • Templates:

    • code samples
    • code sample tests
  • Generalized Testing Harness

    • Mock Inputs
    • Expected Outputs
    • Actual Outputs
    • Test Result
    • Boolean Match Expected vs Actual
      • structure
      • values
  • Test Types

    • Mock
    • Stub
    • Spy
  • Specific

  • Use the linux bash shell to test ANY utility or dev language process output.

https://github.com/dodie/testing-in-bash

  • Script File Name: class-point-min.py
    • Script File Version: 0.1.0
# define named empty class
class Point:
  pass

# create object from class
p1 = Point()
p2 = Point()

# print object
print(p1)
print(p2)

# Was the hardware memory address mismatch expected ??  Why ??

# Add attributes to those objects.
p1.x = 3; p1.y = 2
p2.x = 1; p2.y = 2

print(p1.x, p1.y)
print(p2.x, p2.y)

# Several functions support testing.
def defineOutputDataStructure():
def matchExpectedStructure():
def matchExpectedValues():
def getResult():
def formattedOutput():

points_dict = { 'p1': [p1.x, p1.y], 'p2': [p2.x, p2.y] }

output_data_structure = { 'p1': [p1.x, p1.y], 'p2': [p2.x, p2.y] }

expected_output = { 'p1': [3,2], 'p2': [1,2] }

assert 

bash => py3

description bash python3
Assign text to var $ BASH_STRING='Hello Bash!' >>> python_string = 'Hello Python3!'
Print to stdout $ echo "${BASH_STRING}" >>> print(python_string)
Print to file $ echo "${BASH_STRING}" > $FILE >>> print(python_string, end="", file='hello.py')
Print to file append $ echo "${BASH_STRING}" >> $FILE >>> print(python_string)
Create temp test directory $ mkdir ~/temp-tests/; cd $~ >>> import system ???
times
performance
dates
maths
Load a library $ source bash_functions.sh >>> import system
Prompt User Input $ read -p 'prompt:' USER_TEXT >>> user_text = input("prompt")
Parse Args $ echo $@ >>> argparse(??)
Count Args $ echo $# >>> argparse(??)
File exists + writeable + ascii + not null $ [ -z $FILENAME ] >>> ??
Set Memory Usage $ ulimit
Track Memory Usage $ watch free >>> memory_profiler
Function Signature
Text generators + concatenators $ echo ${VAR1}.${VAR2} >>> print(var1 + var2)
  • To deduce emerging-current-legacy python3 versions...

  • To check python version from bash: $ python3 --version

  • To run a python script from bash: $ python3 py3-script.py

  • To run a python script from bash, ending up in python3 interactive terminal: $ python3 -i python3-script.py

  • To learn about which platform python3 is running on: $ python3 -m platform

  • To install python3 modules from bash: $ python3 -m pip install pyspark

  • To load python3 module: import pyspark

  • Script File Name: class-point-min.py

    • Script File Version: 0.1.0
# define named empty class
class Point:
  pass

# create object from class
p1 = Point()
p2 = Point()

# print object
print(p1)
print(p2)

# Was the hardware memory address mismatch expected ??  Why ??

# Add attributes to those objects.
p1.x = 3; p1.y = 2
p2.x = 1; p2.y = 2

print(p1.x, p1.y)
print(p2.x, p2.y)

# Several functions support testing.
def defineInputDataStructure():
def defineOutputDataStructure():
def matchExpectedStructure():
def matchExpectedValues():
def getResult():
def formattedOutput():

points_dict = { 'p1': [p1.x, p1.y], 'p2': [p2.x, p2.y] }
output_data_structure = { 'p1': [p1.x, p1.y], 'p2': [p2.x, p2.y] }
expected_output = { 'p1': [3,2], 'p2': [1,2] }
assert 

https://docs.python.org/3/library/argparse.html

Web Playground Features

Feature Components

  • Roles + Realms:

  • User Stories

  • Diagrams --E

  • Input

  • Processing

  • Output

  • Dependencies

  • Testing: Templates + Stubs + Mocks + Spies + Tools

  • Interfaces

  • Flows

    • Environments
    • Target
    • Engage
    • Read
    • Search Wide (relevance)
    • Search Deep (significance)
    • Iterate
    • Dojos
    • AI support
    • Discuss Experiments
    • Discuss Labs
    • Training
    • i18n
    • Internships
    • Projects

Component Generics

  • models => data
  • controllers => operations
  • views => interfaces
  • workflows => debug => parallelize
  • programmign paradigms

Web Playground Features

  • Playground Environment Support

    • Environments: Dev => QA => UAT => Production
    • Python Version Support: 3.[7-11]
    • Editor Support
    • Code Macro Views
    • Install Python Modules
    • Web PLayground REST API Roadmap
  • Session Support

    • Platform Configs + Scenarios
    • Test Driven Learning Ladders
    • Playground Session States: Save + Restore
    • Playground Session Behaviors: Save + Replay + Unplay
    • Shares + Invitations: Sync + Async... spectator + participant + leaders
    • Session Timelines
  • Derive Diagrams --E

  • Generate diagrams in parallel with infra + deploys + workloads

  • Impose Diagrams

Zen of Python

``` >>> import this ````

  • The Zen of Python, by Tim Peters

    • Rule: Beautiful is better than ugly.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Explicit is better than implicit.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Simple is better than complex.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Complex is better than complicated.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Flat is better than nested.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Sparse is better than dense.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Readability counts.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Special cases aren't special enough to break the rules.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Although practicality beats purity.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Errors should never pass silently.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Unless explicitly silenced.

      • Rule Example:
      • Rule Exception Example:
    • Rule: In the face of ambiguity, refuse the temptation to guess.

      • Rule Example:
      • Rule Exception Example:
    • Rule: There should be one-- and preferably only one --obvious way to do it.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Although that way may not be obvious at first unless you're Dutch.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Now is better than never.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Although never is often better than right now.

      • Rule Example:
      • Rule Exception Example:
    • Rule: If the implementation is hard to explain, it's a bad idea.

      • Rule Example:
      • Rule Exception Example:
    • Rule: If the implementation is easy to explain, it may be a good idea.

      • Rule Example:
      • Rule Exception Example:
    • Rule: Namespaces are one honking great idea -- let's do more of those!

      • Rule Example:
      • Rule Exception Example:
  • Python Koans

https://www.pythontutorial.net/python-oop/python-__repr__/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment