Examples#
Let’s rewrite some of our C++ codes in python to see how the language works.
Counting and more#
Python lists have a count method. Let’s implement our Counting repetitions
example.
Given a list a, we can just use a.count():
>>> a = [1, 1, 4, 6, 10, 1, 3, -2, -8, 0]
>>> a.count(1)
3
What about doing a count_if? That’s a little more tricky, but
we can do a comprehension:
>>> num = len([e for e in a if e < 0])
>>> num
2
enumerate#
The rough equivalent of std::ranges::find is the index method.
If we want to find the indices for multiple matches, we can use enumerate to loop over a list and get both the index and list element at the same time.
Using the same list as above:
>>> a = [1, 1, 4, 6, 10, 1, 3, -2, -8, 0]
>>> indices = []
>>> for n, e in enumerate(a):
... if e == 1:
... indices.append(n)
...
>>> indices
[0, 1, 5]
This can be done as a comprehension as well:
>>> indices = [n for n, e in enumerate(a) if e == 1]
>>> indices
[0, 1, 5]
Tip
C++23 has an enumerate function available in the standard library.
Root-finding#
Let’s rewrite our Bisection root-finding method in python.
This is almost a one-to-one translation. The main thing we change is that
root is now passed back as the return value, and it is set to None
if the root could not be found. We can do this in python because
we don’t need to specify the types. In C++ this is harder.
ATOL = 1.e-14
RTOL = 1.e-14
MAX_ITER = 100
# Apply bisection to find a root of f(x) in the range [xleft, xright]
# Return value is None if there was an error
def bisection(xleft, xright, f):
fleft = f(xleft)
fright = f(xright)
if fleft * fright > 0:
print("no root in interval [xleft, right]")
return None
if fleft == 0 or fright == 0:
print("root is one of bounds")
return None
xmid = 0.5 * (xleft + xright)
iter = 0
while iter < MAX_ITER:
fmid = f(xmid)
if fleft * fmid > 0.0:
# root is in [xmid, xright]
xleft = xmid
fleft = fmid
else:
# root is in [xleft, xmid]
xright = xmid
err = abs(xright - xleft)
xmid = 0.5 * (xleft + xright)
# check if we've converged
if err < RTOL * abs(xmid) + ATOL:
return xmid
iter += 1
# check to see if we took too many iterations
print("too many iterations")
return None
def f(x):
return x**3 + x**2 - 1
def main():
# bisection on f(x) = x^3 + x^2 - 1
print("trying bisection on f(x) = x^3 + x^2 - 1")
root = bisection(-5.0, 5.0, f)
if root is not None:
print(f"x_0 = {root}; f(x_0) = {f(root)}\n")
else:
print("root not found")
main()
Note that we end with a call to main(). This is the part that will be executed
if we run this as:
python bisection
We can make it such that we can either run this on the command line (as above) or import
the module (i.e., import bisection) by changing this to:
if __name__ == "__main__":
main()
Here, __name__ is a built-in that will only be equal to __main__ if we are
running from the commandline.
Extended solar system class#
We want to implement the functionality from Extending our Solar System Class.
In particular, we want our Planet class to have methods for adding and getting
a planet, and computing the period.
import math
class Planet:
def __init__(self, name, a=1, e=0):
self.name = name
self.a = a
self.e = e
def __str__(self):
return f"{self.name:10} : {self.a:5.3f}, {self.e:5.3f}"
class SolarSystem:
def __init__(self, mass):
assert mass > 0
self.star_mass = mass
self.planets = []
def add_planet(self, name, a, e=0.0):
if any(p.name == name for p in self.planets):
raise ValueError("planet already exists")
self.planets.append(Planet(name, a, e))
def print_planets(self):
for p in self.planets:
print(p)
def get_planet(self, name):
p = [p for p in self.planets if p.name == name]
if len(p) > 0:
return p[0]
print(f"planet {name} does not exist")
return None
def get_period(self, name):
p = self.get_planet(name)
if p:
return math.sqrt(p.a**3 / self.star_mass)
return None
if __name__ == "__main__":
ss = SolarSystem(2.0)
ss.add_planet("alpha", 1.0)
ss.add_planet("beta", 1.5, 0.1)
ss.add_planet("gamma", 3.0, 0.24)
ss.print_planets()
print(f"period of alpha = {ss.get_period('alpha')}")
# note that get_planet returns a reference to the Planet in our
# solar system, so if we update it, we the change is reflected
print()
print("changing eccentricity of beta")
p = ss.get_planet("beta")
p.e = 0.9
ss.print_planets()
# and what happens if we try to get a planet that doesn't exist?
print()
print("accessing a planet that doesn't exist")
p2 = ss.get_planet("earth")
print(p2)
Some comments:
in
add_planet, we do:any(p.name == name for p in self.planets)
this if equivalent to:
found = False for p in self.planets: if p.name == name: found = True break
this is an example of comprehension.
in
get_planetwe get ourPlanetas:p = [p for p in self.planets if p.name == name]
this creates a list with the entries in
self.planetsthat have the namename. If there are no suchPlanetobjects, then the list will be empty (so we changelen(p).Also note that
phere is the same object as inself.planets, we can think about it as a reference.