Object Abstraction
Polymorphic Functions
Polymorphic function: A function that applies to many (poly) different forms (morph) of data.
Example: String Representationstr and repr are both polymorphic; they apply to any object
reprinvokes a zero-argument method__repr__on its argumentstrinvokes a zero-argument method__str__on its argument
Implementing str and repr
The behaviour of repr, different from invoking __repr__:
- An instance attribute called
__repr__is ignored - Only class attributes are found
def repr(x):
return type(x).__repr__(x) # The __repr__ of its classThe behaviour of str, different from invoking __str__:
- An instance attribute called
__str__is ignored - If no
__str__attribute is found, usesreprstring
When we define a class in Python, __str__ and __repr__ are both built-in methods for the class.
We can call those methods using the global built-in functions str(obj) or repr(obj) instead of dot notation, obj.__repr__() or obj.__str__().
In addition, the print() function calls the __str__ method of the object and displays the returned string with the quotations removed, while simply calling the object in interactive mode in the interpreter calls the __repr__ method and displays the returned string with the quotations removed.
class Rational:
def __init__(self, numerator, denominator):
self.numerator = numerator
self.denominator = denominator
def __str__(self):
return str(self.numerator) + '/' + str(self.denominator)
def __repr__(self):
return 'Rational' + '(' + str(self.numerator) + ',' + str(self.denominator) + ')'
>>> a = Rational(1, 2)
>>> [str(a), repr(a)]
['1/2', 'Rational(1,2)']
>>> print(a)
1/2
>>> a
Rational(1,2)Interfaces
[!!TODO:TODO] easify this An object interface is a collection of attributes and conditions on those attributes. Different instances of the same class must have the same object interface.
Inherited attributes and methods share the same name because the base and the subclass both implement the same interface.
For the sake of object abstraction, interfaces should be designed to be robust to all special implementations of the class. That is, however the subclass behaviour differently from the base class, the design should abstract away the implementation details and make the interface achieve the same effect.
Message Passing: Objects interact by looking up attributes on each other (passing messages)
The attribute look-up rules allow different data types to respond to the same message. A shared message (attribute name) that elicits similar behaviour from different object classes is a powerful method of abstraction.
An interface is a set of shared messages, along with a specification of what they mean.
__repr__and__str__methods that return Python-interpretable and human-readable strings implement an interface for producing string representations
Special Methods
In Python, certain special names are invoked by the Python interpreter in special circumstances.
e.g.
__init__: automatically invoked whenever an object is constructed__str__: invoked when printing__repr__: invoked in an interactive session to display values__add__: invoked to add one object to another__float__: invoked to convert an object to a float
>>> zero, one, two = 0, 1, 2
>>> one.__add__(two)
3 # Same with 1 + 2
>>> zero.__bool__(), one.__bool__()
(False, True) # Same with bool(zero), bool(one)Interfaces can be built for user-defined objects to implement built-in functions and operators, by overriding the special methods.
Boolean Method for Object
By default, objects of user-defined classes are considered to be true. __bool__ method can override this behaviour, by evaluating some conditions to false on invoking the method.
>>> Account.__bool__ = lambda self: self.balance != 0
>>> bool(Account('Jack'))
False
>>> if not Account('Jack'):
print('Jack has nothing')
Jack has nothingSequnce Operations
The len function invokes the __len__ method of its argument to determine its length. All built-in sequence types implement this method.
>>> len('Go Bears!')
9
>>> 'Go Bears!'.__len__()
9Python uses a sequence's length to determine its truth value, if it does not provide a __bool__ method. Empty sequences are false, while non-empty sequences are true.
The __getitem__ method is invoked by the element selection operator, but it can also be invoked directly.
>>> 'Go Bears!'[3]
'B'
>>> 'Go Bears!'.__getitem__(3)
'B'Callable Objects
In Python, functions are first-class objects, so they can be passed around as data and have attributes like any other object. Python also allows us to define objects that can be "called" like functions by including a __call__ method.
>>> class Adder(object):
def __init__(self, n):
self.n = n
def __call__(self, k):
return self.n + k
>>> add_three_obj = Adder(3)
>>> add_three_obj(4)
7Arithmetic
Special methods can also define the behavior of built-in operators applied to user-defined objects.
e.g. + operator First, Python checks for an __add__ method on the value of the left operand, then checks for an __radd__ method on the value of the right operand. If either is found, that method is invoked with the value of the other operand as its argument.
Here's an example of type dispatching, where you inspect the type of an argument to decide what implementation to take.
def Rational.__add__(self, other):
if isinstance(other, int):
n = self.numer + self.denom * other
d = self.denom
elif isinstance(other, Rational):
n = self.numer * other.denom + self.denom * other * numer
d = self.denom * other.denom
g = gcd(n, d)
elif isinstance(other, float):
return float(self) + other
return Rational(n//g, d//g)
Rational.__radd__ = Rational.__add__
def gcd(n, d):
while n != d:
n, d = min(n, d), abs(n-d)
return n
### OR ###
def gcd(n, d):
if b == 0:
return a
return gcd(b, a % b)
### OR ###
gcd = lambda a, b: (a if b == 0 else gcd(b, a % b))Here's an example of type coercion, where the object is converted into another type in order to be able to combine it with some other value.
def Rational.__add__(self, other):
...
elif isinstance(other, float):
return float(self) + other
...
def Rational.__float__(self):
return self.numer / self.denomMultiple Representations
https://www.composingprograms.com/pages/27-object-abstraction.html#multiple-representations
Generic Functions
https://www.composingprograms.com/pages/27-object-abstraction.html#generic-functions