Object Oriented Programming

classes

Copy Copy to clipboad
Downlaod Download
#!/usr/bin/env python3

# self is the default reference for an instance
class Simple:
 data = 1.0
 def setData(self, newData):                     # a member function
    self.data = 0.0 if newData<0.0 else newData
#   self.data = newData
 def aboutClass(preString):                      # a static function
    return preString + " A simple class!"

# () default constructor (without arguments)
c1 = Simple()                                    # creating an instance
c2 = Simple()
c2.setData(3.0)                                  # calling a member function
print("c1/c2 data      :  ", c1.data, c2.data)
print("c1 type         :  ", type(c1))
print("c1.data type    :  ", type(c1.data))
print("id(c1)          :  ", id(c1))
print("id(c2)          :  ", id(c2))
print()
print(Simple.aboutClass("# main :: "))           # calling a static function

class magic functions

Copy Copy to clipboad
Downlaod Download
#!/usr/bin/env python3

# a pretty empty class
class empty:
 pass                     # passing (do nothing)

# dynamic creation of new attributes for an existing instance
# -- bad programming --
empty.name = "Alice"
empty.birthDate = "2025"
print("name/date   : ", empty.name, empty.birthDate)

print()
print("listing of class magic functions") 
print("includes user defined attributes") 
print()
for mF in dir(empty):     # dir: directory of magic methods
  print(mF)

instantiation and initialization

Copy Copy to clipboad
Downlaod Download
#!/usr/bin/env python3

# 'self' in Python corresponds to 'this' in C++/Java
class testInit:
 def __init__(self, name, age=None):     # defines 'self'
   self.name = name
   if age==None:
     self.age = 0
   else:
     self.age = age

 def __del__(self):                      # destructor
   print("deleting an instance of ", self.__class__.__name__)

 def hello(self):
   print("Hello for the first time\n")

 def hello(self):                        # overides first definition
   print("Hello for the second time\n")

def nothing():                           # just a function
   print("| entering nothing")          
   cc = testInit("Julia") 
   print("| leaving  nothing")           # destructor called by default

print("*******************************")
print("instance creation in a function")
print("*******************************")
print()
nothing()
print()

print("*************************")
print("basic constructor testing")
print("*************************")
c1 = testInit("Julia")                   # both constructors possible
c2 = testInit("Alice", 10)
c3 = testInit(age=33, name="Kenys")      # calling with keys
c1.hello()                               # which one is called?
print("c1  :  ",c1.name, c1.age)
print("c2  :  ",c2.name, c2.age)
print("c3  :  ",c3.name, c3.age)
del c1, c2, c3                           # deletes them

print()
print("***************")
print("__new__ testing")
print("***************")
c4 = object.__new__(testInit)            # object without attributes created
print(c4.__dict__)                       # dictionary of attributes

c4.__init__('John')                      # create attributes, don't call twice
print(c4.__dict__)

print()
print("c4  :  ",c4.name, c4.age)
# print(dir(c4))                         # for comparison

inheritance

Copy Copy to clipboad
Downlaod Download
#!/usr/bin/env python3

# a class acting as parent class
class Person:
 "docstring of Person  class"
 def __init__(self, first, last):
   self.firstName = first
   self.lastName = last

 def printName(self):
   print(self.firstName, self.lastName)

# a child class of 'Person'
# calling '_init__' of parent class
class Student(Person):
 "docstring of Student class"

 def __init__(self, ff_name, ll_name, studentNumber = None):
   super().__init__(ff_name, ll_name) 
   if studentNumber==None:
     self.studentNumber = 0
   else:
     self.studentNumber = studentNumber

p1 = Person("John", "Doe")
p1.printName()

s1 = Student("Jane", "Ark")
s1.printName()                             # inherited 

print()
print(p1.__doc__)                          # docstrings
print(s1.__doc__)                          # docstrings
print()

decorators

Copy Copy to clipboad
Downlaod Download
#!/usr/bin/env python3

import math

# -----------------
# a nested function
# -----------------
def outer(x):
  print("# in outer, x   : ", x)
# x += 10
  def inner(y):
    print("# in inner, x, y :", x, y)
    return x + y
  return inner                # outer returns inner

print("# *** ****************** ")
print("# *** calling  outer(5)") 
print("# *** ****************** ")
add_five = outer(5)

print("\n# *** ****************** ")
print("# *** calling  inner(7)") 
print("# *** ****************** ")
result = add_five(7)

print("\n# *** ****************** ")
print("# *** calling  outer(3)(6)") 
print("# *** ****************** ")
outer(3)(6)

print()
print("printing outer(4)(8) ", outer(4)(8))

# -----------------
# functions as objects
# -----------------

def add(x, y):
    return x + y

def times(x, y):
    return x * y

def calculate(func, x, y):
    return func(x, y)

print("\n")
print("# *** ******************** ")
print("# *** functions as objects ")
print("# *** ******************** ")

print("# adding      4 6 ", calculate(add  , 4, 6))
print("# multiplying 4 6 ", calculate(times, 4, 6))

# -----------------
# decorators
# -----------------
def doSomething(anyFunc):
  def inner(x):
    return 1.0*anyFunc(x+0.0)
  return inner

@doSomething           # decorating the subsequent function
def newExp(x):
 return math.exp(x)

print("\n")
print("# *** ******************** ")
print("# *** decorators           ")
print("# *** ******************** ")

print("calling newExp(0) ", newExp(0.0))

timing with decorators

Copy Copy to clipboad
Downlaod Download
#!/usr/bin/env python3

import math
from funcy.debug import print_durations  
from funcy.debug import log_durations
# funcy = functional Python

def useLogMessage(log_message):
  """do something with the log message, 
     like logging to a file"""
  print("# time logging: ", log_message)


@log_durations(useLogMessage)     # for any function call / block
# @print_durations()              # directly to console
def hardWork(nIter):
  xx = 0.0
  for _ in range(nIter):
    xx = 1.0/(1.0+xx) 
  return xx

# --- -------------------------------
# --- main guarding, executed only 
# --- if current thread == __main__
# --- but not if this code is imported
# --- -------------------------------

print("# my __name__ :  ", __name__)
print("# my __file__ :  ", __file__)
print()
if __name__ == "__main__":

# result of hardWork() in the limit N\to\innfty
  infiniteN = 0.5*(math.sqrt(5.0)-1)

  print(hardWork(int(1e1)))
  print(hardWork(int(1e8)))
  print()
  print(infiniteN)