#import the necessary libraries
import abc
class Rectangle:
"""An abtract representation of a rectangle"""
#Attributes
p1 = (0, 0)
p2 = (1,2)
#Methods
def area(self) :
return abs(self.p1[0] - self.p2[0])* abs(self.p1[1] - self.p2[1])
#Setters like Java
def setP1(self, p1) :
self.p1 = p1
def setP2(self, p2) :
self.p2 = p2
Rectangle.area()
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[478], line 1 ----> 1 Rectangle.area() TypeError: Rectangle.area() missing 1 required positional argument: 'self'
rectangle = Rectangle()
(rectangle.p1, rectangle.p2 )
((0, 0), (1, 2))
rectangle.p1 = (2,1)
rectangle.area()
1
rectangle.setP1((0,0))
rectangle.area()
2
class Rectangle:
"""An abtract representation of a rectangle"""
#Constructor
def __init__(self, p1=(0,0), p2=(1,2)) :
self.p1 = p1
self.p2 = p2
#Methods
def area(self) :
return abs(self.p1[0] - self.p2[0])* abs(self.p1[1] - self.p2[1])
#Setters like Java
def setP1(self, p1) :
self.p1 = p1
def setP2(self, p2) :
self.p2 = p2
rectangle = Rectangle()
rectangle.area()
2
rectangle.p2 = (2,2)
rectangle.area()
4
class RectanglePrivate:
"""An abtract representation of a rectangle"""
#Constructor
def __init__(self, p1=(0,0), p2=(1,2)) :
self.__p1 = p1 #private
self.p2 = p2 #public
#Methods
def area(self) :
return abs(self.__p1[0] - self.p2[0])* abs(self.__p1[1] - self.p2[1])
#Setters like Java
def setP1(self, p1) :
self.__p1 = p1
def setP2(self, p2) :
self.p2 = p2
rectangle = RectanglePrivate()
rectangle.area()
2
rectangle.p1
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[153], line 1 ----> 1 rectangle.p1 AttributeError: 'RectanglePrivate' object has no attribute 'p1'
rectangle.__p1
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In[154], line 1 ----> 1 rectangle.__p1 AttributeError: 'RectanglePrivate' object has no attribute '__p1'
dir(rectangle)
['_RectanglePrivate__p1', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'area', 'p2', 'setP1', 'setP2', 'tete', 'toto']
rectangle.__doc__
'An abtract representation of a rectangle'
rectangle._RectanglePrivate__p1 = (2,2)
rectangle.setP1((0,0))
rectangle.area()
2
class Rectangle:
"""An object representation of a rectangle"""
# Constructor
def __init__(self, p1 = (0,0), p2 = (1,1)):
self.p1 = p1
self.p2 = p2
# Methods
def area(self):
return abs(self.p1[0] - self.p2[0]) * abs(self.p1[1] - self.p2[1])
# Setters
def setP1(self, p1):
self.p1 = p1
return self
def setP2(self, p2):
self.p2 = p2
return self
Rectangle().area()
1
Rectangle().setP1((-1,-1)).area()
4
def rect_str(self):
return f"Rectangle[{self.p1}, {self.p2}] => area={self.area()}"
Rectangle.__str__ = rect_str
print(Rectangle())
Rectangle[(0, 0), (1, 1)] => area=1
Rectangle()
<__main__.Rectangle at 0x1098a1e10>
str(Rectangle())
'Rectangle[(0, 0), (1, 1)] => area=1'
def rect_repr(self):
return f"Rectangle({self.p1}, {self.p2})"
Rectangle.__repr__ = rect_repr
repr(Rectangle())
'Rectangle((0, 0), (1, 1))'
print(Rectangle())
Rectangle[(0, 0), (1, 1)] => area=1
Rectangle()
Rectangle((0, 0), (1, 1))
class IRectangle(abc.ABC) :
@abc.abstractmethod
def area(self):
pass
@abc.abstractmethod
def perimeter(self):
pass
class Rectangle(IRectangle) :
pass
rect = Rectangle()
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[420], line 1 ----> 1 rect = Rectangle() TypeError: Can't instantiate abstract class Rectangle with abstract methods area, perimeter
IRectangle.__mro__
(__main__.IRectangle, abc.ABC, object)
class Rectangle(IRectangle) :
#Constructor
def __init__(self, p1=(0,0), p2=(1,2)) :
self.p1 = p1
self.p2 = p2
def area(self):
return abs(self.p1[0] - self.p2[0])* abs(self.p1[1] - self.p2[1])
def perimeter(self) :
return (abs(self.p1[0] - self.p2[0]) + abs(self.p1[1] - self.p2[1]) )*2
rect = Rectangle()
rect.area()
2
def call_area(rect: IRectangle) :
return rect.area()
call_area(rect)
2
rect: IRectangle = Rectangle()
rect.area()
2
type(rect)
__main__.Rectangle
class AbstractRectangle(abc.ABC) :
#Constructor
def __init__(self, p1=(0,0), p2=(1,2)) :
self.p1 = p1
self.p2 = p2
@abc.abstractmethod
def area(self):
pass
def perimeter(self) :
return (abs(self.p1[0] - self.p2[0]) + abs(self.p1[1] - self.p2[1]) )*2
rect = AbstractRectangle()
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[179], line 1 ----> 1 rect = AbstractRectangle() TypeError: Can't instantiate abstract class AbstractRectangle with abstract method area
class Rectangle(AbstractRectangle) :
def area(self):
return abs(self.p1[0] - self.p2[0])* abs(self.p1[1] - self.p2[1])
rect = Rectangle()
rect.area()
rect.perimeter()
6
class Square(Rectangle):
pass
square = Square()
square.area()
2
class Square(Rectangle):
def __init__(self, p1=(0,0), l=1):
assert isinstance(l, (float, int)), "l must be a number"
p2 = (p1[0]+l, p1[1]+l)
self.l = l
super().__init__(p1, p2)
def setP1(self, p1):
self.p1 = p1
self.p2 = (self.p1[0]+self.l, self.p1[1]+self.l)
return self
def setP2(self, p2):
raise RuntimeError("Squares take l not p2")
def setL(self, l):
assert isinstance(l, (float, int)), "l must be a number"
self.l = l
self.p2 = (self.p1[0]+l, self.p1[1]+l)
return self
def __repr__(self):
return f"square({self.p1}, {self.l})"
square = Square()
square
square((0, 0), 1)
Square().area()
1
Square().setP1((-1,-1)).area()
1
Square().setL(2).area()
4
Square((0,0), (1,1))
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) Cell In[267], line 1 ----> 1 Square((0,0), (1,1)) Cell In[262], line 3, in Square.__init__(self, p1, l) 2 def __init__(self, p1=(0,0), l=1): ----> 3 assert isinstance(l, (float, int)), "l must be a number" 4 p2 = (p1[0]+l, p1[1]+l) 5 self.l = l AssertionError: l must be a number
Square().setL((0,0))
--------------------------------------------------------------------------- AssertionError Traceback (most recent call last) Cell In[268], line 1 ----> 1 Square().setL((0,0)) Cell In[262], line 17, in Square.setL(self, l) 16 def setL(self, l): ---> 17 assert isinstance(l, (float, int)), "l must be a number" 18 self.l = l 19 self.p2 = (self.p1[0]+l, self.p1[1]+l) AssertionError: l must be a number
Square().setP2((0,0))
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[269], line 1 ----> 1 Square().setP2((0,0)) Cell In[262], line 14, in Square.setP2(self, p2) 13 def setP2(self, p2): ---> 14 raise RuntimeError("Squares take l not p2") RuntimeError: Squares take l not p2
class Rectangle:
"""An object representation of a rectangle"""
# Constructor
def __init__(self, p1 = (0,0), p2 = (1,1)):
self.p1 = p1
self.p2 = p2
# Methods
def area(self):
return abs(self.p1[0] - self.p2[0]) * abs(self.p1[1] - self.p2[1])
# implemented methods
def __iter__(self):
return iter([self.p1, (self.p1[0], self.p2[1]), self.p2, (self.p2[0], self.p1[1])])
# Setters
def setP1(self, p1):
self.p1 = p1
return self
def setP2(self, p2):
self.p2 = p2
return self
for pt in Rectangle():
print(pt)
(0, 0) (0, 1) (1, 1) (1, 0)
class Rectangle:
def __init__(self, p1 = (0,0), p2 = (1,1)):
self.p1 = p1
self.p2 = p2
self.vertices = [self.p1, (self.p1[0], self.p2[1]),
self.p2, (self.p2[0], self.p1[1]) ]
self.index = 0
# Methods
def area(self):
return abs(self.p1[0] - self.p2[0]) * abs(self.p1[1] - self.p2[1])
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.vertices):
self.index = 0
raise StopIteration
v = self.vertices[self.index]
self.index += 1
return v
def __repr__(self):
return f"Rectangle({self.p1}, {self.p2})"
def __str__(self) :
return f"Rectangle[{self.p1}, {self.p2}] => area={self.area()}"
# Setters
def setP1(self, p1):
self.p1 = p1
return self
def setP2(self, p2):
self.p2 = p2
return self
r = Rectangle()
for pt in r:
print(pt)
(0, 0) (0, 1) (1, 1) (1, 0)
r.area()
1
r
Rectangle((0, 0), (1, 1))
dir(r)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'area', 'index', 'p1', 'p2', 'setP1', 'setP2', 'vertices']
class Worm:
def __init__ (self, name) :
self.name = name
def eat(self):
print(self.name +" swallows")
class Fly:
def __init__ (self, name) :
self.name = name
def move(self):
print("I'm flying")
def eat(self):
print(self.name +" is nibbling...")
class ButterFly(Worm, Fly):
def __init__(self, name):
self.name = name
butterfly = ButterFly("BoBo")
butterfly.eat()
BoBo swallows
class ButterFly(Fly, Worm):
def __init__(self, name):
self.name = name
butterfly = ButterFly("BoBo")
butterfly.eat()
BoBo is nibbling...
Method Resolution Order
The method resolution order (or MRO) tells Python how to search for inherited methods. This comes in handy when you’re using super() because the MRO tells you exactly where Python will look for a method you’re calling with super() and in what order.
Every class has an __mro__ attribute that allows us to inspect the order, so let’s do that:
dir(FlyingReptile)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'eat', 'eat_bird', 'fly']
FlyingReptile.__mro__
(__main__.FlyingReptile, __main__.Reptile, __main__.Bird, object)
Rectangle.__mro__
(__main__.Rectangle, object)
class ButterFly(Worm, Fly):
def __init__(self, name):
self.name = name
def eat_likefly(self):
return Fly.eat(self) ## or super(Worm, self).eat() in some cases
butterfly = ButterFly("Butterfly")
butterfly.eat_likefly()
Butterfly is nibbling...
fly = Fly("Fly")
fly.eat()
Fly is nibbling...
FlyingReptile.__mro__
(__main__.FlyingReptile, __main__.Reptile, __main__.Bird, object)
butterfly.eat()
Butterfly swallows
class AbstractWorm(abc.ABC):
def __init__ (self, name) :
self.name = name
@abc.abstractmethod
def eat(self, food):
pass
class AbstractFly(abc.ABC):
def __init__ (self, name) :
self.name = name
@abc.abstractmethod
def fly(self):
pass
@abc.abstractmethod
def eat(self):
pass
class AbstractButterFly(AbstractFly, AbstractWorm):
pass
butterfly = AbstractButterFly("BoBo")
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[457], line 4 1 class AbstractButterFly(AbstractFly, AbstractWorm): 2 pass ----> 4 butterfly = AbstractButterFly("BoBo") TypeError: Can't instantiate abstract class AbstractButterFly with abstract methods eat, fly
class AbstractButterFly(AbstractFly, AbstractWorm):
def eat(self) :
pass
def fly(self):
pass
butterfly = AbstractButterFly("BoBo")
butterfly.eat()
class AbstractButterFly(AbstractFly, AbstractWorm):
def eat(self) :
print(self.name +" is nibbling")
def fly(self):
print(self.name + "is flying... goodbye.." )
butterfly = AbstractButterFly("BoBo")
butterfly.eat()
BoBo is nibbling
class AbstractWorm(abc.ABC):
def __init__ (self, name) :
self.name = name
@abc.abstractmethod
def eat(self):
pass
class AbstractFly(abc.ABC):
def __init__ (self, name) :
self.name = name
@abc.abstractmethod
def fly(self):
pass
def eat(self):
print("Bird "+self.name + " is nibbling")
AbstractWorm()
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[470], line 1 ----> 1 AbstractWorm() TypeError: Can't instantiate abstract class AbstractWorm with abstract method eat
class AbstractButterFly(AbstractFly, AbstractWorm):
def fly(self):
print(self.name + "is flying... goodbye.." )
butterfly = AbstractButterFly("BoBo")
butterfly.eat()
Bird BoBo is nibbling
class AbstractButterFly(AbstractWorm, AbstractFly):
def fly(self):
print(self.name + "is flying... goodbye.." )
butterfly = AbstractButterFly("BoBo")
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[473], line 4 2 def fly(self): 3 print(self.name + "is flying... goodbye.." ) ----> 4 butterfly = AbstractButterFly("BoBo") TypeError: Can't instantiate abstract class AbstractButterFly with abstract method eat
class Animal:
def display(self) :
print("I'm a cute animal")
class Dog(Animal) :
def display(self) :
print("I'm a cute dog animal")
dog = Dog()
dog.display()
I'm a cute dog animal
Dog.__mro__
(__main__.Dog, __main__.Animal, object)
dir(Dog)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'display']