Object Orientation
                     vs.
          Functional Programming
           Writing Modular Python Programs




Twitter: @insmallportions
www.insmallportions.com
About Me
Modularity
Roadmap
Thesis                            Antithesis
Object Orientation is a proven    Functional Programming is a
way of creating models in         long standing approach to
software that represent the       defining processes in terms of
problem domain in a useful        others at different levels of
manner. There are many            abstraction. Higher order
patterns that show how to         functions make the idiomatic
achieve the modularity goal in    ways to perform certain tasks
different contexts.               fall out naturally.

                        Synthesis
Python has good support for both styles of programming and
for good reason. Depending on the situation one or the other
maybe more appropriate. Moreover in Python these tools do
not only exist but they complement each other.
Object Orientation

● Class Oriented

● The Three Pillars of
   OO are:
1. Delegation
2. Polymorphism
3. Instantiation
Template Method
class Game(object):                           class Monopoly(Game):
   PLAYERS = 2                                   PLAYERS = 4
   def initialize_game(self):                    def initialize_game(self):
     raise NotImplementedError()                   pass # Monopoly code here
   def make_play(self, player):                  def make_play(self, player):
     raise NotImplementedError()                   pass # Monopoly code here
   def end_of_game(self):                        def end_of_game(self):
     raise NotImplementedError()                   pass # Monopoly code here
   def print_winner(self):                       def print_winner(self):
     print self.current                            pass # Monopoly code here
   def play_game(self, players=PLAYERS):      class Chess(Game):
     self.initialize_game()                      def initialize_game(self):
     self.current = 0                              pass # Chess code here
     while not self.end_of_game():               def make_play(self, player):
         self.make_play(self.current)              pass # Chess code here
         self.current = (self.current + 1)      def end_of_game(self):
            % players                              pass # Chess code here
     self.print_winner()
Abstract Base Classes
>>> class MyDict(dict):           >>> from collections import Mapping
... def __getitem__(self, key):   >>> class >>> from collections import
...   return 101                  Mapping
...                               >>> class MyMapping(Mapping):
>>> d = MyDict()                  ... def __getitem__(self, key):
>>> d['x']                        ...      return 101
101                               ...
>>> d.get('x', 202)               >>> m = MyMapping()
202                               Traceback (most recent call last):
>>>                                 File "<stdin>", line 1, in <module>
                                  TypeError: Can't instantiate abstract
                                  class MyMapping with abstract
                                  methods __iter__, __len__
Mixins
class XMPPClient(object):            class OnlineChess
   def connect(self):                (Game,              XMPPClient):
     pass # XMPP code                   def initialize_game(self):
   def disconnect(self):                  pass # Chess code here
     pass # XMPP code
   def send(self, player):             ...
     pass # XMPP code                  def end_of_game(self):
   def terminate(self, player):          pass # Chess code here
     raise NotImplementedError()
                                       def terminate(self, player):
  def mainain_presence(self):            return self.end_of_game()
    self.connect()
    while not self.terminate():
       yield
    self.disconnect()
Mixins (Multiple Inheritance)
class A(object):
   pass

class B(A):
   def method1(self):
     pass

class C(A):
   def method1(self):
     pass

class D(B, C):
   pass
Wrapping/Composition
● Prefer Composition over Inheritance
● Use a class's functionality but not its API
● Expose only limited part of an object

● Typical uses:
   ○ Adapt
   ○ Proxy
   ○ Decorate
Wrapping/Composition
class Eprom(object):             class SafeEprom(object):
   def read(self):                  def __init__(self, eprom):
     pass # Eprom code                self._eprom = eprom
   def write(self, data):           def read(self):
     pass # Eprom code                return self._eprom.
   def complete(self):           read()
     pass # Eprom code              def write(self, data):
                                      if data.safe():
class FileLikeEprom(object):              self._eprom.write
   def __init__(self, eprom):    (data)
     self._eprom = eprom            def close(self):
   def read(self):                    self._eprom.complete()
     return self._eprom.read()
   def write(self, data):
     self._eprom.write(data)
   def close(self):
     self._eprom.complete()
Wrapping/Composition (Tricks)

● Don't Repeat Yourself   class FileLikeEprom(object):
                             def __init__(self, eprom):
● Avoid boilerplate
                               self._eprom = eprom
● Use __getattr__ to         def __getattr__(self, a):
  return computed              if a == 'close':
  attributes                       return self.close
                               else:
                                   return getattr(self._eprom,
                          a)
                             def close(self):
                               self._eprom.complete()
Mixins Again
class SafeAndFileLike(FileLikeEprom, SafeEprom):
   def __init__(self, *args, **kwargs):
     return super(SafeAndFileLike, self).__init__(*args,
**kwargs)
Roadmap
Thesis                           Antithesis
Object Orientation is a proven   Functional Programming is a
way of creating models in        long standing approach to
software that represent the      defining processes in terms of
problem domain in a useful       others at different levels of
manner. There are many           abstraction. Higher order
patterns that show how to        functions make the idiomatic
achieve the modularity goal in   ways to perform certain tasks
different contexts.              fall out naturally.

                       Synthesis
Python good support for both styles of programming and for
good reason. Depending on the situation one or the other
maybe more appropriate. Moreover in Python these tools do
not only exist but they complement each other.
Functional Programming

● Functions take input and
  produce output, without
  any side effects.
● Pure functional languages
  are strict about side effect
  freeness.
● Python is not a pure
  functional language.
● Functions may be
  internally imperative, but
  appear purely functional
  in their behaviour.
Callbacks

● The Hollywood principle
● Role reversal, library code calls your code
● Library code accepts a callable and invokes it
  when appropriate

● The main uses:
   ○ Customisation
   ○ Event Handling
sorted() sans Callbacks
class Person(object):                  >>> people = [Person
   def __init__(self, f, s):           ('John', 'Smith'),
     self.f = f                        ... Person('Mary', 'Doe'),
     self.s = s                        ... Person('Lucy', 'Pearl'),]
   def __str__(self):                  >>> for p in sorted(people):
     return '%s %s' % (self.f, self.   ... print p
s)                                     ...
   def __eq__(self, other):            Mary Doe
     return self.s == other.s          Lucy Pearl
   def __lt__(self, other):            John Smith
     return self.s < other.s           >>>
sorted() with Callbacks
class Person(object):                  >>> for p in sorted(people,
   def __init__(self, f, s):           key=first_name):
     self.f = f                        ... print p
     self.s = s                        ...
   def __str__(self):                  John Smith
     return '%s %s' % (self.f, self.   Lucy Pearl
s)                                     Mary Doe
                                       >>> for p in sorted(people,
first_name = lambda p: p.f             key=surname_name):
surname = lambda p: p.s                ... print p
                                       ...
                                       Mary Doe
                                       Lucy Pearl
                                       John Smith
                                       >>>
operator module
                    from operator import attrgetter
● attrgetter
● itemgetter        class Person(object):
● add                  def __init__(self, f, s):
● mul                    self.f = f
                         self.s = s
● pow                  def __str__(self):
                         return '%s %s' % (self.f, self.
                    s)

                    first_name = attrgetter('f')
                    surname = attrgetter('s')
Operations on collections of objects

                 >>> def square(x):
● sum            ...     return x ** 2
● filter         ...
● map            >>> l = [1, 2, 3, 4, 5]
● reduce         >>> sum(map(square, l))
                 55
                 >>> def square(x):
                 ...    return x ** 2
                 ...
                 >>> def odd(x):
                 ...    return x % 2
                 ...
                 >>> l = [1, 2, 3, 4, 5]
                 >>> sum(map(square, filter(odd, l)))
                 35
itertools module
● cycle()
● repeat()
● chain()
● tee()
● product()
● and many more...
Decorators
def cache(fn, c=None):            def cache(fn, c=None):
  if c is None: c = {}              if c is None: c = {}
  def cached(*args):                def cached(*args):
      if args in c:                     if args in c:
          return c[args]                    return c[args]
      result = fn(*args)                result = fn(*args)
      c[args] = result                  c[args] = result
      return result                     return result
  return cached                     return cached

def adder(x, y):                  @cache
  return x + y                    def adder(x, y):
                                    return x + y
adder = cache(adder)
Do not write code like this, use: functools.lru_cache
Partial function evaluation
>>> from functools import partial
>>>
>>> def power(base, exp=1):
...     return base ** exp
...
>>> square = partial(power, exp=2)
>>> cube = partial(power, exp=3)
>>>
>>> l = [1, 2, 3, 4, 5]
>>> sum(map(square, l))
55
>>> print sum(map(cube, l))
225
Roadmap
Thesis                           Antithesis
Object Orientation is a proven   Functional Programming is a
way of creating models in        long standing approach to
software that represent the      defining processes in terms of
problem domain in a useful       others at different levels of
manner. There are many           abstraction. Higher order
patterns that show how to        functions make the idiomatic
achieve the modularity goal in   ways to perform certain tasks
different contexts.              fall out naturally.

                       Synthesis
Python good support for both styles of programming and for
good reason. Depending on the situation one or the other
maybe more appropriate. Moreover in Python these tools do
not only exist but they complement each other.
Best of Both Worlds
Unbound methods
>>> food = ['Spam', 'ham',           ● Functions are descriptors
'Cheese', 'eggs']                    ● Override binding behaviour
>>> sorted(food)                     ● Override differently for A.x
['Cheese', 'Spam', 'eggs', 'ham']      and a.x
>>> sorted(food, key=str.lower)      ● Unbound methods know
['Cheese', 'eggs', 'ham', 'Spam']      their class but not their
>>>                                    instance
                                     ● Ideal for use in a functional
                                       style
>>> sorted(food, key='ham'.
lower)
Traceback (most recent call last):
  File "<stdin>", line 1, in
<module>
TypeError: lower() takes no
Computed fields (property)
                                     class Person(object):
class Person(object):                   def __init__(self, f, s):
   def __init__(self, f, s):              self.f = f
     self.f = f                           self._s = s
     self.s = s                         @property
   @property                            def s(self):
   def fullname(self):                    return self._s.upper()
     return '%s %s' % (self.f,          @s.setter
        self.s)                         def s(self, value):
                                          self._s = value
>>> p = Person('John', 'Smith')
>>> p.fullname                       >>> p = Person('Jane',
'John Smith'                         'Doe')
                                     >>> p.s
                                     'DOE'
             property([fget[, fset[, fdel[, doc]]]])
property and inheritance
class Person(object):                 class Person(object):
   def __init__(self, t, f, s):          def __init__(self, t, f, s):
      ...                                   ...
   def full(self):                       def full(self):
      return '%s %s' % (self.f,             return '%s %s' % (self.f,
          self.s)                               self.s)
   fullname = property(full)             def _full(self):
                                            return self.full()
class Customer(Person):                  fullname = property(_full)
   def full(self):
      return '%s. %s %s'              class Customer(Person):
%                  (self.t, self.f,      def full(self):
self.s)                                     return '%s. %s %s'
                                      %                  (self.t, self.f,
 >>> c = Customer('Mr',               self.s)
'John', 'Smith')
>>> c.fullname                        >>> c.fullname
Dependency Inversion
class Employee(object):        def employee_fact(f, s):
   def __init__(self, f, s):     return Employee(f, s)
     self.f = f
     self.s = s                def register(emps, fact):
   def register(self):           for f, s in emps:
     pass # Register me             emp = fact(f, s)
                                    emp.register()
def register(emps):
  for f, s in emps:            >>> emps = [('John',
     emp = Employee(f, s)      'Smith'), ('Mary', 'Doe')]
     emp.register()            >>>register(emps,
                               employee_fact)
>>> emps = [('John',
'Smith'), ('Mary', 'Doe')]
>>>register(emps)
Python classes are factories
class Employee(object):          ● Python classes are callables
   def __init__(self, f, s):     ● Indistinguishable from other
     self.f = f                    callables to the caller
     self.s = s                  ● Allow us to postpone the
   def register(self):             creation of a factory until it
     pass # Register me            actually needed

def register(emps, fact):
  for f, s in emps:
     emp = fact(f, s)
     emp.register()

>>> emps = [('John', 'Smith'),
('Mary', 'Doe')]
>>>register(emps,
Employee)
Many types of callables
                                class Callable(object):
  ● Functions                      def __init__(self, m):
  ● Unbound methods                  self.message = m
                                   def __call__(self):
  ● Bound methods                    print self.message
  ● Classes                     class NotCallable(object):
 ● Any object that has a           def call(self):
   __call__ method is a              print "You Rang?"
   callable
 ● Testable using the           >>> c = Callable('You Rang')
                                >>> c()
   callable built-in function
                                You Rang
                                >>> n = NotCallable()
>>> callable(str)               >>> n()
True                            Traceback (most recent call last):
>>> callable('Spam')             File "<stdin>", line 1, in <module>
False                           TypeError: 'NotCallable' object is not
>>>                             callable
Roadmap
Thesis                           Antithesis
Object Orientation is a proven   Functional Programming is a
way of creating models in        long standing approach to
software that represent the      defining processes in terms of
problem domain in a useful       others at different levels of
manner. There are many           abstraction. Higher order
patterns that show how to        functions make the idiomatic
achieve the modularity goal in   ways to perform certain tasks
different contexts.              fall out naturally.

                         Synthesis
Python good support for both styles of programming and for
good reason. Depending on the situation one or the other
maybe more appropriate. Moreover in Python these tools do not
only exist but they complement each other nicely.
We hire superheroes!

● www.demonware.net/jobs/

● Development & Operations
  Positions

● Come talk to me any time
  or meet me at the hiring
  event