Hi Jurgis --
I've tried various approaches with K-12, noting that's in itself a wide spectrum i.e. K is nothing like 12.
I'll focus on high school (9-12).
I'd say the ubiquity (omnipresence) of the "dot operator" as "accessor" suggests working it into ordinary mathematical thinking, at first as a way to reach "attributes" (properties) and second as a way to trigger behaviors. noun.attribute versus noun.behavior(). Why isn't "dot notation" part of everyday math? Given the influx of CS in lower grades, I'd say it's becoming so, defacto.
One mathematical object that calls out for such treatment is the Polyhedron, since that's obviously mathematical anyway, and is clearly an Object in the most literal sense. Polyhedrons are colorful and accessible and help make that bridge to game engines providing visuals.
>>> tet = Tetrahedron() # could come with many defaults
>>> tet.edges
6
>>> tet.vertexes
4
>>> tet.faces
4
What behaviors do we expect from a polyhedron?
For starters:
tet.rotate(degrees, axis)
tet.scale()
tet.translate(vector).
We see how surface area increases as a 2nd power of change in linear scale, volume as a 3rd power e.g.:
>>> tet.volume
1
>>> bigtet = tet.scale(2) # double all edge lengths
>>> bigtet.volume
8
>>> biggest = tet.scale(3) # triple all edge lengths
>>> biggest.volume
27
Your earlier focus on sorting might still be used, as a typical behavior of mathematical objects, such as lists.
Your focus is perhaps less on the algorithms used and more on the syntax e.g. .items() as a behavior of the dict type.
That segment in Python where we show how to sort on any element of a tuple might be worth covering:
>>> polyvols = {"tetrahedron":1, "octahedron":4, "cube":3, "cuboctahedron":20}
>>> vols_tuples = tuple(polyvols.items())
>>> vols_tuples
(('tetrahedron', 1), ('octahedron', 4), ('cube', 3), ('cuboctahedron', 20))
>>> sorted(vols_tuples) # alphabetical
[('cube', 3), ('cuboctahedron', 20), ('octahedron', 4), ('tetrahedron', 1)]
>>> sorted(vols_tuples, key=lambda t: t[1]) # by volume, get to use lambda
[('tetrahedron', 1), ('cube', 3), ('octahedron', 4), ('cuboctahedron', 20)]
another way, not using lambda, shown in the docs on sorting:
>>> from operator import itemgetter
>>> getvol = itemgetter(1) # get item 1 of a tuple or other object using __getitem__
>>> getvol(('tetrahedron', 1))
1
>>> sorted(vols_tuples, key=getvol)
[('tetrahedron', 1), ('cube', 3), ('octahedron', 4), ('cuboctahedron', 20)]
Like Wes said, you could bridge to inheritance here (if in Python, interfaces are less required as we have multiple inheritance).
class Polyhedron # base class
could implement the generic guts of a polyhedron with subclasses like:
class Tetrahedron(Polyhedron)
class Cube(Polyhedron)
class Octahedron(Polyhedron)
class Cuboctahedron(Polyhedron)
...
The special names __lt__ __eq__ __gt__ for <, ==, > will even let you implement sorting, in say volume order.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 15 08:31:57 2018
@author: Kirby Urner
"""
class Polyhedron:
def __lt__(self, other):
return self.volume < other.volume
def __gt__(self, other):
return self.volume > other.volume
def __eq__(self, other):
return self.volume == other.volume
def scale(self, factor):
return type(self)(v=self.volume * factor**3)
def __repr__(self):
return "{}(v={})".format(type(self).__name__, self.volume)
class Tetrahedron(Polyhedron):
"Self dual, space-filler with octahedron"
def __init__(self, v=1):
self.volume = v
self.edges, self.vertexes, self.faces = (6, 4, 4)
class Cube(Polyhedron):
"Dual of Octahedron, space-filler"
def __init__(self, v=3):
self.volume = v
self.edges, self.vertexes, self.faces = (12, 8, 6)
class Octahedron(Polyhedron):
"Dual of Cube, space-filler with tetrahedron"
def __init__(self, v=4):
self.volume = v
self.edges, self.vertexes, self.faces = (12, 6, 8)
class RhDodecahedron(Polyhedron):
"Dual of Cuboctahedron, space-filler"
def __init__(self, v=6):
self.volume = v
self.edges, self.vertexes, self.faces = (24, 14, 12)
class Cuboctahedron(Polyhedron):
"Dual of Rh Dodecahedron"
def __init__(self, v=20):
self.volume = v
self.edges, self.vertexes, self.faces = (24, 12, 14)
mypolys = (Tetrahedron(), Cuboctahedron(), Octahedron(), Cube())
volume_order = sorted(mypolys)
print(volume_order)
from operator import attrgetter
name = attrgetter("__class__.__name__")
# >>> name(Tetrahedron())
# 'Tetrahedron'
name_order = sorted(mypolys, key= name)
print(name_order)
OUTPUT:
By volume: [Tetrahedron(v=1), Cube(v=3), Octahedron(v=4), Cuboctahedron(v=20)]
By name: [Cube(v=3), Cuboctahedron(v=20), Octahedron(v=4), Tetrahedron(v=1)]
Note:
these are not necessarily familiar volumes outside a curriculum informed by American literature.
My art-science courses tend to phase in the above conventions under the rubric of "Martian Math".
Kirby