Machine Learning Primer -- Python Tutorial




Claudius Gros, WS 2024/25

Institut für theoretische Physik
Goethe-University Frankfurt a.M.

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' could be replaced, e.g. by 'this'
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 += 10
  def inner(y):
    print("# in inner", x, y)
    return x + y
  return inner                # outer returns inner

add_five = outer(5)
result = add_five(6)
print("# in main ", result)
print(add_five) 
print() 
print("#==========================") 
print() 

#
# 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("# adding         ", calculate(add  , 4, 6))
print("# multiplication ", calculate(times, 4, 6))
print() 
print("#==========================") 
print() 

#
# decorators
#
def addSomething(anyFunc):
  def inner(x):
    print("# inner, doing something ")
    return anyFunc(x)
  return inner

@addSomething           # decorating the following function
def newExp(x):
 return math.exp(x)

print("# calling newExp ", newExp(7.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

# may need to be installed
# pip install funcy
# funcy -- functionaly 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

golden = 0.5*(math.sqrt(5.0)-1)
# actually, the golden ratio is
#        0.5*(math.sqrt(5.0)+1)

print(hardWork(int(1e5)))
print(hardWork(int(1e7)))
print(golden)