| Container |
Generic base for containers that supports the |
| Iterable |
Base for objects that support iteration, e.g., provide |
| Sequence | Objects that support indexing and slicing |
| Mapping | Base for dictionaries that supports key-based lookup and iteration. |
#-- demonstrate std collection types with generic hints
def demo_std_generics():
Anal.showNote(" demo std generics")
print()
Anal.showOp("list[int]")
l1: list[int] = [1, 2, 3, 2, 1]
print(type(l1))
print(l1)
print()
Anal.showOp("list[str]")
l2: list[str] = ["you", "me", "them", "us"]
print(type(l2))
print(l2)
print()
Anal.showOp("tuple(int, float, str)")
t: tuple[int, float, str] = (42, -0.5, "forty two")
print(type(t))
print(t)
print()
Anal.showOp("dict[str, int]")
d: dict[str, int] = {
"zero":0, "one":1, "two":2, "three":3
}
print(type(d))
print(d)
print()
--------------------------------------------------
demo std generics
--------------------------------------------------
--- list[int] ---
<class 'list'>
[1, 2, 3, 2, 1]
--- list[str] ---
<class 'list'>
['you', 'me', 'them', 'us']
--- tuple(int, float, str) ---
<class 'tuple'>
(42, -0.5, 'forty two')
--- dict[str, int] ---
<class 'dict'>
{'zero': 0, 'one': 1, 'two': 2, 'three': 3}
#-- user defined Hello type --#
from typing import TypeVar, Generic
T = TypeVar('T', bound=int | float)
class Hello(Generic[T]):
# supports constructor notation
def __init__(self, t: T = 0) -> None:
self._datum:T = t
# property supports public member syntax while
# keeping _datum encapsulated, e.g., not in
# public interface
@property
def value(self):
return self._datum
@value.setter
def value(self, t:T):
self._datum = t
@value.getter
def value(self):
return self._datum
# show named value of Point[T] instance.
# Uses default value for left.
def show(self, name, left = 0) :
print(AnalysisGen.indent(left), sep='', end='')
print(name, ": ", "Hello { ", self._datum, " }", sep='')
#-- demonstrate user-defined generic types --#
def demo_user_defined_generics():
Anal.showNote(" demo user-defined generics")
print()
Anal.showOp("Hello(42)")
h1: Hello[int] = Hello(42)
print(" ", type(h1))
h1.show("h1", 2)
print()
Anal.showOp("Hello() uses default argument")
h2: Hello[float] = Hello()
h2.show("h2", 2)
print()
Anal.showOp("h2.value = 3.1415927")
h2.value = 3.1415927
h2.show("h2", 2)
print()
/* points code elided */
}
--------------------------------------------------
demo user-defined generics
--------------------------------------------------
--- Hello(42) ---
<class '__main__.Hello'>
h1: Hello { 42 }
--- Hello() uses default argument ---
h2: Hello { 0 }
--- h2.value = 3.1415927 ---
h2: Hello { 3.1415927 }
#----------------------------------------------------------
# Py_Generic::PointsGen.py
# Point[T]
# Point in N dimensional hyperspace with type T
# coordinates.
#----------------------------------------------------------
import datetime
import time
import AnalysisGen
from typing import TypeVar, Generic
# Generic point class with N coordinates
# Python doesn't need generics to support types
# defined at translation time, but they were
# introduced to enable translation time type
# error detection.
T = TypeVar('T', bound=int | float)
class Point(Generic[T]):
# supports constructor notation
def __init__(self, n: int) -> None: # p = Point<double>(8)
self.coors:list[T] = []
for i in range(n):
self.coors.append(0)
self._dt: datetime = datetime.datetime.now()
def append(self, t: T):
self.coors.append(t)
def len(self) -> int:
return len(self.coors)
def __len__(self) -> int: # len(p)
return len(self.coors)
def __getitem__(self, key: int) -> T: # value = p[1]
return self.coors[key]
def __setitem__(self, key: int, val: T): # p[1] = value
self.coors[key] = val
@property
def dt(self):
return self._dt
@dt.getter
def dt(self):
return self._dt
def now(self):
_dt = datetime.datetime.now()
# show named value of Point[T] instance
def show(self, name, left = 2, width = 7) :
print(AnalysisGen.indent(left), name, ' ', sep='')
print(AnalysisGen.indent(left), "{", sep='')
print(AnalysisGen.fold(self.coors, left+3, width), sep='')
print(AnalysisGen.indent(left + 2), self.dt)
print(AnalysisGen.indent(left), "}", sep = '')
#-- demonstrate user-defined generic types --#
def demo_user_defined_generics():
Anal.showNote(" demo user-defined generics")
print()
/* Hello code elided */
Anal.showOp("p1 = Points.Point[float](5)")
p1: Points.Point = Points.Point[float](5)
p1[1] = 1.5
p1[3] = -2.0
index = p1.len() - 1
p1[index] = 42
print(" ", type(p1))
p1.show("p1", 2)
print()
}
--------------------------------------------------
demo user-defined generics
--------------------------------------------------
--- p1 = Points.Point[float](5) ---
<class 'PointsGen.Point'>
p1
{
0, 1.5, 0, -2.0, 42
2024-07-24 21:46:01.293971
}
#----------------------------------------------------------
# Py_Generic::AnalysisGen.py
# - Collection of display and analysis functions
#----------------------------------------------------------
import sys
import collections
from collections.abc import Sequence
from typing import TypeVar
T = TypeVar('T')
# Coll = Union[Iterable, Sized] = 'Coll'
# Python requires definition before use ordering
# - no link phase to find definitions
import copy
nl = "\n"
# show name, type, value, and size of a Python instance
def showType(t:T, nm:str, indnt:int = 2, suffix:str = "") :
print(indent(indnt), nm, ' ', type(t), " dynamic", sep="")
print(indent(indnt), "value: ", t, ', size: ', sys.getsizeof(t), suffix, sep="")
# generate indent string with n spaces
def indent(n:int):
tmpStr = ""
for i in range(n):
tmpStr += ' '
return tmpStr
# fold indexable into rows of width elements indented by
# left spaces
def fold(enum: Sequence, left:int, width:int) -> str:
tmpStr = indent(left)
for i in range(len(enum)):
tmpStr += str(enum[i]) + ", "
if(((i + 1) % width) == 0 and i != 0):
tmpStr += "\n" + indent(left)
rIndex = tmpStr.rindex(',')
tmpStr = tmpStr[:rIndex]
return tmpStr
# show name, type, value, and size of a Python instance
def showTypeEnum(enum:Sequence, nm:str, left:int = 2, width:int = 7, suffix:str = "") :
# topStr = indent(left) + nm + type(enum) + "dynamic"
print(indent(left),nm, ' ', type(enum), ' ', "dynamic", sep='')
print(indent(left), "{", sep='')
print(fold(enum, left+2, width))
print(indent(left), "}", sep = '')
print(indent(left), "size: ", sys.getsizeof(enum), suffix, sep='')
# same as showType except uses class method to show value
def showTypeShowable(t:T, nm:str, suffix:str = ""):
print(type(t), "dynamic")
t.show(nm)
# show Python id, unique for each instance
def showIdent(t:T, name:str, suffix:str = "") :
print(name, '"{}"'.format(t), id(t), suffix)
# show emphasized note
def showNote(text:str, suffix:str = "", n: int = 50) :
tmpStr = ""
for i in range(n):
tmpStr += '-'
print(tmpStr)
print(text)
print(tmpStr, suffix)
# show delineated string to announce a program operation
def showOp(text:str):
print("--- {} ---".format(text))
def demo_generic_functions():
Anal.showNote(" demo generic functions", "\n")
Anal.showOp("Anal.showType(list[int], name)")
l1:list[int] = [1, 2, 3, 2, 1, 0, -1, -2, -1, 0]
Anal.showType(l1, "l1")
print()
Anal.showOp("showTypeEnum(list[int], name)")
Anal.showTypeEnum(l1, "l1", left = 2, width = 7, suffix = "")
print()
--------------------------------------------------
demo generic functions
--------------------------------------------------
--- Anal.showType(list[int], name) ---
l1 <class 'list'> dynamic
value: [1, 2, 3, 2, 1, 0, -1, -2, -1, 0], size: 136
--- showTypeEnum(list[int], name) ---
l1 <class 'list'> dynamic
{
1, 2, 3, 2, 1, 0, -1,
-2, -1, 0
}
size: 136
C:\github\JimFawcett\Bits\Python\Py_Generic
> mypy AnalysisGen.py
AnalysisGen.py:55: error: "T" has no attribute "show" [attr-defined]
Found 1 error in 1 file (checked 1 source file)
C:\github\JimFawcett\Bits\Python\Py_Generic
#----------------------------------------------------------
# Py_Generic.py
#
# Python type hints and generics
# - Demonstrates generic and type hint syntax
# - Shows how to build user-defined generic type
# - Type hints are ignored by the Python interpreter
# - To check for type errors use tool mypy, e.g.:
#
# mypy Py_Generic.py --check-untyped-defs
#
# mypy requires installation using pip
#----------------------------------------------------------
# Python std types
# list, tuple, range, dict, set,
import copy
import AnalysisGen
import PointsGen
# short-hand identifiers
Anal = AnalysisGen
Points = PointsGen
#-- demonstrate std collection types with generic hints
def demo_std_generics():
# code elided, shown above
#-- user defined Hello type --#
from typing import TypeVar, Generic
T = TypeVar('T', bound=int | float)
class Hello(Generic[T]):
# code elided, shown above
#-- demonstrate user-defined generic types --#
def demo_user_defined_generics():
# code elided, shown above
def demo_generic_functions():
# code elided, shown above
#-- demonstration starts here --#
def execute() :
Anal.showNote(" Demonstrate Python Generics")
Anal.showNote(
" Python introduced generics and type hints\n"\
" to check for type errors using mypy, a static\n"\
" type checking tool."
)
print()
demo_std_generics()
demo_user_defined_generics()
demo_generic_functions()
print("\nThat's all folks!\n")
execute()
C:\github\JimFawcett\Bits\Python\Py_Generic
> python Py_Generic.py
--------------------------------------------------
Demonstrate Python Generics
--------------------------------------------------
--------------------------------------------------
Python introduced generics and type hints
to check for type errors using mypy, a static
type checking tool.
--------------------------------------------------
--------------------------------------------------
demo std generics
--------------------------------------------------
--- list[int] ---
<class 'list'>
[1, 2, 3, 2, 1]
--- list[str] ---
<class 'list'>
['you', 'me', 'them', 'us']
--- tuple(int, float, str) ---
<class 'tuple'>
(42, -0.5, 'forty two')
--- dict[str, int] ---
<class 'dict'>
{'zero': 0, 'one': 1, 'two': 2, 'three': 3}
--------------------------------------------------
demo user-defined generics
--------------------------------------------------
--- Hello(42) ---
<class '__main__.Hello'>
h1: Hello { 42 }
--- Hello() uses default argument ---
h2: Hello { 0 }
--- h2.value = 3.1415927 ---
h2: Hello { 3.1415927 }
--- p1 = Points.Point[float](5) ---
<class 'PointsGen.Point'>
p1
{
0, 1.5, 0, -2.0, 42
}
--------------------------------------------------
demo generic functions
--------------------------------------------------
--- Anal.showType(list[int], name) ---
l1 <class 'list'> dynamic
value: [1, 2, 3, 2, 1, 0, -1, -2, -1, 0], size: 136
--- showTypeEnum(list[int], name) ---
l1 <class 'list'> dynamic
{
1, 2, 3, 2, 1, 0, -1,
-2, -1, 0
}
size: 136
That's all folks!
C:\github\JimFawcett\Bits\Python\Py_Generic
C:\github\JimFawcett\Bits\Python\Py_Generic
> python Py_Generic.py
| Reference | Description |
|---|---|
| Python Type Hints | Syntax for type hinting - requires static analysis tool mypy.py to catch type errors. |
| Python Abstract Base Classes | Specification for building Python's equivalent of Interfaces |
| Python Tutorial - w3schools | Interactive examples |
| Python Reference - docs.python.org | Semi-formal syntax reference |
Hello[T]:
⇐ Type Hints ⇐ "generic" class using type hint ⇐ "private"