Skip to content

Think Python, Chs. 16, 17

Reading Notes


Pure Functions

Modifiers

  • These are functions that modify the parameter objects.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def increment(time, seconds):
    time.second += seconds

    if time.second >= 60:
        time.second -= 60
        time.minute += 1

    if time.minute >= 60:
        time.minute -= 60
        time.hour += 1

Anything that can be done with modifiers can be done also with pure functions.

Functional Programming:

Write pure functions whenever it is reasonable; resort to modifiers only if there is a compelling advantage.

Designed Development

  • Example is of writing the time conversion function using Base 60 arithmetic.

Debugging

  • invariants -- requirements that should always be true. If they are not true, something has gone wrong.

  • Assert statement -- checks a given invariant, raises exception if the check fails.

1
2
3
4
def add_time(t1, t2):
    assert valid_time(t1) and valid_time(t2)
    seconds = time_to_int(t1) + time_to_int(t2)
    return int_to_time(seconds)

Classes & Methods

Object-oriented Features

  • Object-oriented programming language :

    • Programs include class and method definitions;
    • Most computation is expressed in terms of operations on objects;
    • Objects can represent real-world things; methods can correspond to real-world interactions/operations.
  • A method is a function associated with a particular class.

  • Syntactic differences between methods and functions:

    • Methods are defined inside a class definition to make the relationship between method-class explicit.
    • Invoking a method takes different syntax from calling a function.

Printing Objects

1
2
3
class Time:
    def print_time(time):
        print('%.2d:%.2d:%.2d' % (time.hour, time.minute, time.second))
1
2
3
4
5
>>> Time.print_time(start)
09:45:00

>>> start.print_time()
09:45:00

Sometimes, shifting responsibility from the functions ontothe objects makes it possible to write more versatile functions (or methods), and makes it easier to maintain and reuse code.

Another example

1
2
3
4
5
# inside class Time

    def increment(self, seconds):
        seconds += self.time_to_int()
        return int_to_time(seconds)
1
2
3
4
5
6
>>> start.print_time()
09:45:00

>>> end = start.increment(1337)
>>> end.print_time()
10:07:17
  • The subject, start, gets assigned to the first parameter, self.
  • The argument, 1337, gets assigned to the second parameter, seconds.

  • A positional argument is one without a parameter name; not a keyword argument.

More complex example

  • Here, is_after() takes two Time objects as parameters. It is conventional to name the first parameter self, and the second, other.
1
2
3
4
# inside class Time

    def is_after(self, other):
        return self.time_to_int() > other.time_to_int()
1
2
>>> end.is_after(start)
True

The init method

  • A special method, invoked when an object is instantiated. The full name is __init__().
1
2
3
4
5
6
# inside class Time

    def __init__(self, hour=0, minute=0, second=0):
        self.hour = hour
        self.minute = minute
        self.second = second

self.hour = hour stores the value of the parameter hour as an attribute of self.

  • The parameters are optional;
  • Calling the class without arguments \(\rightarrow\) default values.

  • Override the first parameter, and it assigns the value to hour:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
>>> time = Time()
>>> time.print_time()
00:00:00

>>> time = Time(9)
>>> time.print_time()
09:00:00

>>> time = Time(9, 45)
>>> time.print_time()
09:45:00

... and so forth.

The __str__ method

  • Will return a string representation of an object.
1
2
3
4
# inside class Time

    def __str__(self):
        return '%.2d:%.2d:%.2d' % (self.hour, self.minute, self.second)
  • Write a new class \(\Rightarrow\) write __init__ for instantiation purposes, and __str__ for debugging purposes.

Operator Overloading

  • Define other special methods to specify the behavior of operators on programmer-defined types.
1
2
3
4
5
# inside class Time

    def __add__(self, other):
        seconds = self.time_to_int() + other.time_to_int()
        return int_to_time(seconds)
1
2
3
4
>>> start = Time(9, 45)
>>> duration = Time(1, 35)
>>> print(start + duration)
11:20:00
  • When we apply the + operator to Time objects, Python invokes __add__. Printing the result invokes __str__.

  • Operator overloading -- changing the behavior of an operator so that it works with programmer-defined types.

Type-based dispatch

  • A version of __add__ that checks the type of other and invokes either add_time or increment:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# inside class Time

    def __add__(self, other):
        if isinstance(other, Time):
            return self.add_time(other)
        else:
            return self.increment(other)

    def add_time(self, other):
        seconds = self.time_to_int() + other.time_to_int()
        return int_to_time(seconds)

    def increment(self, seconds):
        seconds += self.time_to_int()
        return int_to_time(seconds)
  • built-in isinstance takes a value and a class object, returns True if the value is an instance of the class.

  • If other is a Time object, __add__ invokes add_time. Otherwise, it takes the parameter as a number, and invokes increment.

  • This is called type-based dispatch. The computation is sent to different methods based on the argument types.

  • This is not commutative, though. Putting the class object in the first parameter slot will result in a TypeError.

The solution for this is __radd__, or right-side add. It is invoked when a Time object appears on the right side of the + operator.

1
2
3
4
# inside class Time

    def __radd__(self, other):
        return self.__add__(other)

Polymorphism

  • Functions that work with multiple data types are polymorphic.
    • Can facilitate code reuse;

Interface & Implementation

  • Design Principle \(\Rightarrow\) keep interfaces separate from implementations. The methods provided by a class should not depend on how the attributes are represented.