Disclaimer - this is not an OOP course!
Using a tuple or list, it isn't obvious what the elements represent
circle = (25, 15, 7)
Using a dictionary, data integrity is not guaranteed
circle = {'x': 25, 'y': 15, 'radius': -7} # invalid radius
circle['y'] = 'fifteen' # invalid y coordinate
Inability to guarantee the validity of an object is likely the worst downside of purely procedural approaches
class <identifier>:
<suite>
new_object = ClassName()
Arguments to instantiate a Python class are the parameters to its
__init__(.)
method (excluding self
)
new_object = ClassName(arg1, arg2, ...)
Python uses special methods for class operations
By convention, only special methods start and end with two underscores
#!/usr/bin/env python3
class Circle:
def __init__(self, x, y, radius): # special method to initialize object
self.x = x # self is a reference to the current instance
self.y = y
self.radius = radius
def __str__(self): # special method to represent object as string
return (f'Circle(x={self.x}, y={self.y}, radius={self.radius})')
# an instance is created by "calling" the class with its required parameters
a_circle = Circle(25, 15, 7) # ok
print(a_circle)
another_circle = Circle(25, 15, -7) # still invalid
print(another_circle)
#!/usr/bin/env python3
"""
Implementation of a Circle class offering area and circumference methods.
"""
import math
class Circle:
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
def __str__(self):
return (f'Circle(x={self.x}, y={self.y}, radius={self.radius})')
def area(self):
"""Return the area of the circle."""
return math.pi * self.radius * self.radius
def circumference(self):
"""Return the circumference of the circle."""
return 2 * math.pi * self.radius
if __name__ == '__main__':
a_circle = Circle(25, 15, 7)
print(a_circle)
print(f'area: {a_circle.area():.2f}, '
f'circumference: {a_circle.circumference():.2f}')
Allows to restrict access to internal data
Data can only be changed by using methods that guarantee valid operations
Python provides @property
decorators for encapsulting
data fields
Limitation: Python does not offer a bulletproof way of controlling data access
#!/usr/bin/env python3
"""
Implementation of a Circle class offering area and circumference methods.
"""
import math
class Circle:
def __init__(self, x, y, radius):
self.x = x
self.y = y
self.radius = radius
def __str__(self):
return (f'Circle(x={self.x}, y={self.y}, radius={self.radius})')
@property
def radius(self):
return self.__radius
@radius.setter
def radius(self, radius):
"""Ensure that `radius` is valid."""
assert radius >= 0, 'radius must be greater than or equal 0'
self.__radius = radius
def area(self):
"""Return the area of the circle."""
return math.pi * self.radius * self.radius
def circumference(self):
"""Return the circumference of the circle."""
return 2 * math.pi * self.radius
if __name__ == '__main__':
a_circle = Circle(25, 15, 7) # ok
print(a_circle)
another_circle = Circle(25, 15, -7) # not allowed anymore
Using Point
as center of a Circle
#!/usr/bin/env python3
import math
class Point:
def __init__(self, x, y):
self.__x = x
self.__y = y
def __str__(self):
return (f'Point(x={self.x}, y={self.y})')
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
class Circle:
def __init__(self, center, radius):
self.__center = center
self.radius = radius
def __str__(self):
return (f'Circle(center={self.center}, radius={self.radius})')
@property
def center(self):
return self.__center
@property
def radius(self):
return self.__radius
@radius.setter
def radius(self, radius):
"""Ensure that `radius` is valid."""
assert radius >= 0, 'radius must be greater than or equal 0'
self.__radius = radius
def area(self):
"""Return the area of the circle."""
return math.pi * self.radius * self.radius
def circumference(self):
"""Return the circumference of the circle."""
return 2 * math.pi * self.radius
if __name__ == '__main__':
a_circle = Circle(Point(25, 15), 7)
print(a_circle.center)
print(a_circle)
super()
function accesses overridden base
class methodsCircles
and Triangles
are
Shapes
#!/usr/bin/env python3
import collections
import math
class Shape:
"""Every shape has an area and a circumference."""
def area(self):
raise NotImplementedError
def circumference(self):
raise NotImplementedError
class Point:
def __init__(self, x, y):
self.__x = x
self.__y = y
def __str__(self):
return (f'Point(x={self.x}, y={self.y})')
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
def distance(self, other):
"""Return the euclidean distance between self and other."""
delta_x = self.x - other.x
delta_y = self.y - other.y
return math.hypot(delta_x, delta_y)
class Circle(Shape):
def __init__(self, center, radius):
self.__center = center
self.radius = radius
def __str__(self):
return ('Circle(center={self.center}, radius={self.radius})')
@property
def center(self):
return self.__center
@property
def radius(self):
return self.__radius
@radius.setter
def radius(self, radius):
"""Ensure that `radius` is valid."""
assert radius >= 0, 'radius must be greater than or equal 0'
self.__radius = radius
def area(self):
"""Return the area of the circle."""
return math.pi * self.radius * self.radius
def circumference(self):
"""Return the circumference of the circle."""
return 2 * math.pi * self.radius
class Triangle(Shape):
def __init__(self, A, B, C):
self.A = A
self.B = B
self.C = C
def __str__(self):
return f'Triangle({self.A}, {self.B}, {self.C})'
def circumference(self):
return (self.A.distance(self.B) + self.B.distance(self.C) +
self.C.distance(self.A))
def area(self):
"""
Return the area of a triangle.
The area is defined as area = base * height / 2. Alternatively,
a * b * sin(gamma) / 2 can be used.
See http://en.wikipedia.org/wiki/Triangle#Using_trigonometry and
http://en.wikipedia.org/wiki/Angle#Dot_product_and_generalisation.
"""
a = self.C.distance(self.B)
b = self.C.distance(self.A)
Vector = collections.namedtuple('Vector', ['x', 'y'])
CB = Vector(self.B.x - self.C.x, self.B.y - self.C.y)
CA = Vector(self.A.x - self.C.x, self.A.y - self.C.y)
CA_dot_CB = CA.x * CB.x + CA.y * CB.y
gamma = math.acos(CA_dot_CB / (a * b))
return a * b * math.sin(gamma) / 2
if __name__ == '__main__':
a_triangle = Triangle(Point(0, 0), Point(4, 0), Point(0, 3))
a_circle = Circle(Point(25, 15), 7)
shapes = [a_triangle, a_circle]
for shape in shapes:
print(shape)
print(f'area: {shape.area()}, circumference: {shape.circumference()}')
Circles
and Triangles
are
Shapes
#!/usr/bin/env python3
import collections
import math
class Shape:
"""Every shape has an area and a circumference."""
def area(self):
raise NotImplementedError
def circumference(self):
raise NotImplementedError
class Point:
def __init__(self, x, y):
self.__x = x
self.__y = y
def __str__(self):
return (f'Point(x={self.x}, y={self.y})')
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
def distance(self, other):
"""Return the euclidean distance between self and other."""
delta_x = self.x - other.x
delta_y = self.y - other.y
return math.hypot(delta_x, delta_y)
class Circle(Shape):
def __init__(self, center, radius):
self.__center = center
self.radius = radius
def __str__(self):
return ('Circle(center={self.center}, radius={self.radius})')
@property
def center(self):
return self.__center
@property
def radius(self):
return self.__radius
@radius.setter
def radius(self, radius):
"""Ensure that `radius` is valid."""
assert radius >= 0, 'radius must be greater than or equal 0'
self.__radius = radius
def area(self):
"""Return the area of the circle."""
return math.pi * self.radius * self.radius
def circumference(self):
"""Return the circumference of the circle."""
return 2 * math.pi * self.radius
class Triangle(Shape):
def __init__(self, A, B, C):
self.A = A
self.B = B
self.C = C
def __str__(self):
return f'Triangle({self.A}, {self.B}, {self.C})'
def circumference(self):
return (self.A.distance(self.B) + self.B.distance(self.C) +
self.C.distance(self.A))
def area(self):
"""
Return the area of a triangle.
The area is defined as area = base * height / 2. Alternatively,
a * b * sin(gamma) / 2 can be used.
See http://en.wikipedia.org/wiki/Triangle#Using_trigonometry and
http://en.wikipedia.org/wiki/Angle#Dot_product_and_generalisation.
"""
a = self.C.distance(self.B)
b = self.C.distance(self.A)
Vector = collections.namedtuple('Vector', ['x', 'y'])
CB = Vector(self.B.x - self.C.x, self.B.y - self.C.y)
CA = Vector(self.A.x - self.C.x, self.A.y - self.C.y)
CA_dot_CB = CA.x * CB.x + CA.y * CB.y
gamma = math.acos(CA_dot_CB / (a * b))
return a * b * math.sin(gamma) / 2
if __name__ == '__main__':
a_triangle = Triangle(Point(0, 0), Point(4, 0), Point(0, 3))
a_circle = Circle(Point(25, 15), 7)
shapes = [a_triangle, a_circle]
for shape in shapes:
print(shape)
print(f'area: {shape.area()}, circumference: {shape.circumference()}')
__init__(.)
method (excluding self
)