Python and object-oriented programming

(Programação orientada a objetos com Python)

Daniel Moser

Feb 14th, 2017

2nd IAG Python Boot Camp

Table of contents

What is OOP?

OOP is a programming paradigm. It is usually compared with the traditional procedural programming.

The procedural programming is based on procedures (i.e., routines and sub-routines), where the procedures and data variables are independent. Examples: Cobol, C, Fortran...

program Fibonacci;

function fib(n: Integer): Integer;
var a: Integer = 1;
    b: Integer = 1;
    f: Integer;
    i: Integer;
begin
  if (n = 1) or (n = 2) then
     fib := 1
  else
    begin
      for i := 3 to n do
      begin
         f := a + b;
         b := a;
         a := f;
      end;
      fib := f;
    end;
end;

begin
  WriteLn(fib(6));
end.

OOP is based in objects, a variable that combines methods (procedures) and attributes (data).

Examples of OOP compatible languages: C#, C++, Java, Lisp, Python, Ruby...

class Fibonacci:
   def __init__(self):
       self.cache = {}
   def fib(self, n):
       if self.cache.has_key(n):
           return self.cache[n]
       if n == 1 or n == 2:
            return 1
       else:
           a = 1
           b = 1
           for i in range(2, n):
               f = a + b;
               b = a;
               a = f;
           self.cache[n] = f;
           return f;


fibonaccyCounter = Fibonacci()
# print(fibonaccyCounter.fib(600))

# %time fibonaccyCounter.fib(600)
# %time fibonaccyCounter.fib(600)

Why OOP compatible? Because usually in a OOP language one can still work as a procedural language...

Examples of pure OOP languages: Smalltalk and Self.

OOP Concepts: abstraction, class and object

"Class has no life, it's just a concept."

Examples

class testing:
    """This is a IAG Python Boot Camp class definition"""

    def __init__(self, init_value):
        """This is a constructor"""
        self.some_value = init_value
        return

    def double_attr(self):
        """This is a method that double the ``some_value`` atribute"""
        self.some_value *= 2
        return

OOP Concepts: heritage

class Pets(object):
    owner = "Python Boot Camp"

    def __init__(self, name, age, condition='healthy'):
        print("A new pet entry!")
        self.name = name
        self.age = age
        self.condition = condition

    def __repr__(self):
        return "Pet '{0}', {1} year(s)".format(self.name, self.age)

class Dog(Pets):
    def bark(self):
        print(" Woof-woof!!")
        return

class Monkey(Pets):
    def __init__(self, name, age, condition=''):
        """Overwriting the Pets() constructor"""
        condition = 'custom'
        Pets.__init__(self, name, age, condition)

    def gibber(self):
        print(" (gibber)!!")
        return

class Horse(Pets):
    def __init__(self, name, age):
        super(Horse, self).__init__(name, age)

    def neigh(self):
        print(" Neigh!!")
        return

class Cat(Pets):
    def __init__(self):
        super(Pets, self).__init__()

    def meow(self):
        print(" Meow!!")
        return
class Toys:
    color = 'light blue'

class Lassie(Pets, Toys):
    pass

Must read: Python’s super() considered super!

Ways of combining classes and their properties

class A(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def addNums():
        self.b + self.c

class B(object):
    def __init__(self, d, e):
        self.d = d
        self.e = e

    def addAllNums(self, Ab, Ac):
        x = self.d + self.e + Ab + Ac
        return x

ting = A("yo", 2, 6)
ling = B(5, 9)

print ling.addAllNums(ting.b, ting.c)
class A(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def addNums():
        self.b + self.c

class B(object):
    def __init__(self, d, e, A):
        self.d = d
        self.e = e
        self.A = A

    def addAllNums(self):
        x = self.d + self.e + self.A.b + self.A.c
        return x

ting = A("yo", 2, 6)
ling = B(5, 9, ting)

print ling.addAllNums()
class A(object):
    def __init__(self, a, b, c):
        self.a = a
        self.b = b
        self.c = c

    def addNums():
        self.b + self.c

class B(object):
    def __init__(self, d, e):
        self.d = d
        self.e = e
        self.A = A("yo", 2, 6)

    def addAllNums(self):
        x = self.d + self.e + self.A.b + self.A.c
        return x

ling = B(5, 9)

print ling.addAllNums()
Remember: those "association/combination" names may vary in the literature.

Encapsulation and polymorphism

class C(object):
    def __init__(self):
        self.a = 1    # OK to access directly
        self._a = 2   # should be considered private
        self.__a = 3  # considered private, name Disfigured
Warning! Some people say that *encapsulation* can prevent the data from being modified. This is **not** the case in Python (and even Java[!]. Other methods need to used to avoid modification).
class Person(object):
    def pay_bill():
        raise NotImplementedError

class Millionare(Person):
    def pay_bill():
        print "Here you go! Keep the change!"

class GradStudent(Person):
    def pay_bill():
        print "Can I owe you ten bucks or do the dishes?"
import scipy

scipy.interpolate
scipy.interpolate.quad

scipy.integrate

Other OOP Concepts

As far as the function in_the_forest is concerned, the Person object is a duck:

class Duck:
    def quack(self):
        print("Quaaaaaack!")
    def feathers(self):
        print("The duck has white and gray feathers.")

class Person:
    def quack(self):
        print("The person imitates a duck.")
    def feathers(self):
        print("The person takes a feather from the ground and shows it.")
    def name(self):
        print("John Smith")

def in_the_forest(duck):
    duck.quack()
    duck.feathers()

def game():
    donald = Duck()
    john = Person()
    in_the_forest(donald)
    in_the_forest(john)

game()

OOP pro et contra

Advantages of OOP:

Disadvantages of OOP

Extra

This kind of tools can be used, for example, together with abstraction level concepts for automatic programming (or automatic coding)!

Exercises

  1. What dir() do?
  2. What hasattr(), getattr(), setattr() and delattr() do?
  3. What is happening here?
class test:
    def __init__(self):
        print "init 1"

    def __init__(self):
        print "init 2"
  1. How can you change a class (global) attribute?
  2. What isinstance(variable, class) do?
  3. Is Lassie an instance of Pets or Toys?
  4. What instance.__base__ do?
  5. About interfaces. Understand what is happening here:
"""http://python-3-patterns-idioms-test.readthedocs.io/en/latest/ChangeInterface.html"""

class WhatIHave:
    def g(self): pass
    def h(self): pass

class WhatIWant:
    def f(self): pass

class ProxyAdapter(WhatIWant):
    def __init__(self, whatIHave):
        self.whatIHave = whatIHave

    def f(self):
        # Implement behavior using
        # methods in WhatIHave:
        self.whatIHave.g()
        self.whatIHave.h()

class WhatIUse:
    def op(self, whatIWant):
        whatIWant.f()

# Approach 2: build adapter use into op():
class WhatIUse2(WhatIUse):
    def op(self, whatIHave):
        ProxyAdapter(whatIHave).f()

# Approach 3: build adapter into WhatIHave:
class WhatIHave2(WhatIHave, WhatIWant):
    def f(self):
        self.g()
        self.h()

# Approach 4: use an inner class:
class WhatIHave3(WhatIHave):
    class InnerAdapter(WhatIWant):
        def __init__(self, outer):
            self.outer = outer
        def f(self):
            self.outer.g()
            self.outer.h()

    def whatIWant(self):
        return WhatIHave3.InnerAdapter(self)

whatIUse = WhatIUse()
whatIHave = WhatIHave()
adapt= ProxyAdapter(whatIHave)
whatIUse2 = WhatIUse2()
whatIHave2 = WhatIHave2()
whatIHave3 = WhatIHave3()
whatIUse.op(adapt)
# Approach 2:
whatIUse2.op(whatIHave)
# Approach 3:
whatIUse.op(whatIHave2)
# Approach 4:
whatIUse.op(whatIHave3.whatIWant())