What have we been learning so far?
Our focus has been the Python computer language and how to make types or classes.
Once we have a couple Animal classes, we discover the power of inheritance: let your classes "pass the buck" sometimes, to a superclass that's already debugged and trusted. Why reinvent the wheel (or "stomach" or "eat method" as the case may be)?
Let's take another look at the Animals on Replit.
Five Dimensions of Python:
__ribs__
i.e. special names (magic methods)These Jupyter Notebook and JupyterLab based ways of using Python, gaining in popularity, belong to level five.
When we made a Dog type with a way to represent itself, the ways accessing level three features.
Cut and paste the code below to any Python source code editor, either on your local device or in the cloud. Play around with it. Add a new animal type.
"""
Used for Py4HS: Python for High School
https://github.com/4dsolutions/elite_school/blob/master/PY4HS.ipynb
"""
from random import choice # used by Dog type
class Animal:
"""
The parent class. The child classes will get
their instructions from here if they do not
find it in themselves (example: eat method).
"""
def __init__(self, nm): # Python provides a self
"""
the constructor method, a special name
>>> new_python = Python("Shiela")
"""
self.name = nm
self.stomach = [ ]
def eat(self, food):
"""
append to the stomach associated
with this particular instance (self)
"""
self.stomach.append(food)
class Dog(Animal):
"""
Another type
"""
tricks = ['Play dead', 'Fetch', 'Roll over']
def do_trick(self):
return choice(self.tricks)
def __repr__(self):
return f"Dog named {self.name}"
class Python(Animal):
def __repr__(self):
# known as the repper
return "I'm a Python named " + self.name
class Pig(Animal):
"""
A type
"""
def __repr__(self):
return f"Pig named {self.name}"
cyber_dog = Dog("Rover")
robo_dog = Dog("Fido")
cyber_dog.eat("steak")
robo_dog.eat("crackers")
cyber_dog.stomach
['steak']
cyber_dog.eat('🍩') # emoji are strings
cyber_dog.stomach
['steak', '🍩']
A complete understanding of our syntax so far also requires understanding string formatting, which includes the act of string substitution.
Lets take some time to review some of those details.
Our next task is to develop a Rational Number type or class. We'll call it Rat, for Rational, but Rat also reminds us of the animal, which is intentional, because of our coding theme so far.
Lets remember where the rationals fit into our hierarchy of numeric sets:
each a superset of the next previous.
$$ \mathbb{N} \subset \mathbb{W} \subset \mathbb{Z} \subset \mathbb{Q} \subset \mathbb{R} \subset \mathbb{C} $$Lets check a Rat class implementation on Replit.
from math import gcd
class Rat:
def __init__(self, n, d=None):
"""
Allow ints and other Rats to create new Rats
not just the two ints, numerator and denominator
"""
if ( (type(n) == int and not d)
or (type(n) == Rat and not d)):
d = n.denominator
n = n.numerator
GCD = gcd(n, d)
self.numerator = n // GCD
self.denominator = d // GCD
def __mul__(self, other):
new_n = self.numerator * other.numerator
new_d = self.denominator * other.denominator
return Rat(new_n, new_d)
def __add__(self, other):
new_n = self.numerator * other.denominator + other.numerator * self.denominator
new_d = self.denominator * other.denominator
return Rat(new_n, new_d)
def __neg__(self):
return Rat(-self.numerator, self.denominator)
def __sub__(self, other):
return self + (- other)
def __invert__(self):
return Rat(self.denominator, self.numerator)
def __truediv__(self, other):
return self * ~other
def __eq__(self, other):
return ((self.numerator == other.numerator)
and (self.denominator == other.denominator))
def __repr__(self):
return "({} / {})".format(self.numerator, self.denominator)
def __float__(self):
return self.numerator / self.denominator
q = Rat(1, 2)
Rat(q)
(1 / 2)
3 .denominator # the int type has these properties built in
1
Still missing from the above implementation: the ability to put the Rat to the right of the operator in question. We also don't yet have powering i.e. __pow__
. Even without these features, our Rat is fairly functional.
Number sequences figure centrally in this high school curriculum, as they do in so many. Figurate and polyhedral numbers, their gnomons and running totals, primes, Carmichael Numbers... all of these may be researched in the Online Dictionary of Integer Sequences.
What we will discover in this section is that $F[n+1]/F[n]$ where $F[n]$ and $F[n+1]$ are consecutive Fibonacci numbers, approaches ever closer to Phi as n increases.
But first, we need a way to generate those Fibonacci numbers:
def fibo(a=0, b=1):
while True:
yield a
a, b = b, a + b
That's using the keyword yeild
; have you seen that before?
yield
behaves a lot like return, in handing back an object and halting execution.
However, unlike return
, a generator function may resume right after the yield
statement, when nudged by next
.
Let's take a look:
gen_fib = fibo() # create a generator object
next(gen_fib) # run to the next yield
0
next(gen_fib) # run to the next yield (same one)
1
next(gen_fib) # run to the next yield (looping)
1
next(gen_fib) # no limit on how many times we nudge
2
next(gen_fib) # the Fibonacci series is coming out
3
for _ in range(10): # let speed up nudging with for
print(next(gen_fib))
5 8 13 21 34 55 89 144 233 377
denom, numer = next(gen_fib), next(gen_fib)
ratio = Rat(numer, denom) # now build a fraction
ratio
(987 / 610)
float(ratio)
1.618032786885246
The whole (1 + the part) is to 1, as 1 is to the part, what is that part? We can start with it being an unkown and write this equation, equating two ratios:
import sympy as sp
φ = sp.Symbol('φ')
equation = sp.Eq(φ/1, (φ+1)/φ) # 1 is to φ as φ is to x + 1
equation
In class I talked about cross multiplying and how the above would be the same as:
same_equation = sp.Eq(0, φ**2 - φ - 1)
same_equation
This is what we call a polynomial of the 2nd degree, meaning the highest power monomial is 2.
The goal is to find what values of $\phi$ make it a true statement.
sympy will help you with this.
ans = sp.solve(equation)
ans
[1/2 - sqrt(5)/2, 1/2 + sqrt(5)/2]
same_ans = sp.solve(same_equation)
same_ans
[1/2 - sqrt(5)/2, 1/2 + sqrt(5)/2]
print(ans[1].evalf(1000))
1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137484754088075386891752126633862223536931793180060766726354433389086595939582905638322661319928290267880675208766892501711696207032221043216269548626296313614438149758701220340805887954454749246185695364864449241044320771344947049565846788509874339442212544877066478091588460749988712400765217057517978834166256249407589069704000281210427621771117778053153171410117046665991466979873176135600670874807101317952368942752194843530567830022878569978297783478458782289110976250030269615617002504643382437764861028383126833037242926752631165339247316711121158818638513316203840052221657912866752946549068113171599343235973494985090409476213222981017261070596116456299098162905552085247903524060201727997471753427775927786256194320827505131218156285512224809394712341451702237358057727861600868838295230459264787801788992199027077690389532196819861514378031499741106926088674296226757560523172777520353613936
print(sp.S.GoldenRatio.n(1000))
1.618033988749894848204586834365638117720309179805762862135448622705260462818902449707207204189391137484754088075386891752126633862223536931793180060766726354433389086595939582905638322661319928290267880675208766892501711696207032221043216269548626296313614438149758701220340805887954454749246185695364864449241044320771344947049565846788509874339442212544877066478091588460749988712400765217057517978834166256249407589069704000281210427621771117778053153171410117046665991466979873176135600670874807101317952368942752194843530567830022878569978297783478458782289110976250030269615617002504643382437764861028383126833037242926752631165339247316711121158818638513316203840052221657912866752946549068113171599343235973494985090409476213222981017261070596116456299098162905552085247903524060201727997471753427775927786256194320827505131218156285512224809394712341451702237358057727861600868838295230459264787801788992199027077690389532196819861514378031499741106926088674296226757560523172777520353613936
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1+ 1/
(1)))))))))))))))
1.6180327868852458
from fractions import Fraction
one = Fraction(1,1)
one
Fraction(1, 1)
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one+ 1/
(one)))))))))))))))
Fraction(987, 610)