Ch.9: Object-oriented programming Joakim Sundnes 1 , 2 1 Simula Research Laboratory 2 University of Oslo, Dept. of Informatics Oct 20, 2020 0.1 Plan for Oct 24 • Exercises 7.10, 7.11, 7.12 and 7.25 • Object-oriented programming (OOP) – Introduction – Examples 0.2 The title Object-oriented programming (OOP) may mean two different things 1. Programming with classes and objects (better: object- based programming) 2. Programming with class hierarchies (class families) 0.3 New concept: collect classes in families (hierarchies) What is a class hierarchy? • A family of closely related classes • A key concept is inheritance : child classes can inherit attributes and methods from parent class(es) - this saves much typing and code duplication OO is a Norwegian invention by Ole-Johan Dahl and Kristen Nygaard in the 1960s - one of the most important inventions in computer science, because OO is used in all big computer systems today!
0.4 Object-oriented programming • Everything in Python is an object, so all Python-programming is object- based • Object-oriented programming (OOP) takes the ideas of classes and pro- gramming a step further • We exploit a very useful property of classes; that they can be combined and reused as building blocks. • If we define a class class A , we can define a second class class B(A) . • Class B inherits all attributes and methods from A • Class becomes an extension of class A • We say that A is a superclass or base class, and B is a subclass of A 0.5 OOP in Python and scientific programming • OO is less important in Python than in C++, Java and C#, so the benefits of OO are less obvious in Python � b • Our examples on OOP employ numerical methods for a f ( x ) dx , f ′ ( x ), u ′ = f ( u, t ) - make sure you understand the simplest of these numerical methods before you study the combination of OOP and numerics • Our goal: write general, reusable modules with lots of methods for numeri- � b a f ( x ) dx , f ′ ( x ), u ′ = f ( u, t ) cal computing of 0.6 OOP fundamentals - inheritance class A : def __init__(self,v0,v1) self.v0 = v0 self.v1 = v1 def f(self, x): return x**2 class B (A): def g(self, x): return x**4 class C (B): def h(self, x): return x**6 2
We have now defined three classes • A: two attributes ( v0, v1 ) and two methods ( __init__ , f ) • B: two attributes ( v0, v1 ) and three methods ( __init__ , f , g ) • C: two attributes ( v0, v1 ) and four methods ( __init__ , f , g , h ) 0.7 Why is OOP more important in other languages? Languages like Java and C++ have static typing. A function is declared to take input arguments of a certain type: void my_func(A my_obj) { ... } OOP gives extra flexibility, since this function will also accept arguments of classes B and C . OOP is still very useful in Python, to avoid code duplication and produce structured and readable code! 0.8 OOP fundamentals - overriding methods A subclass can override methods in the superclass. Say we want to add some extra attributes in a subclass: class A : def __init__(self,v0,v1) self.v0 = v0 self.v1 = v1 def f(self, x): return x**2 class B (A): def __init__(self,v0,v1,v2): self.v0 = v0 self.v1 = v1 self.v2 = v2 def g(self, x): return x**4 Usage: a = A(v0=1, v1=2) #calling A.__init__ b = B(v0=1, v1=2, v3=3) #calling B.__init__ 3
0.9 The overridden method can still be called A more elegant implementation looks like: class A : def __init__(self,v0,v1) self.v0 = v0 self.v1 = v1 def f(self, x): return x**2 class B (A): def __init__(self,v0,v1,v2): super().__init__(v0,v1) #or A.__init__(self.v0,v1) self.v2 = v2 def g(self, x): return x**4 0.10 Example: a class for straight lines Problem: Make a class for evaluating lines y = c 0 + c 1 x . Code: class Line : def __init__(self, c0, c1): self.c0, self.c1 = c0, c1 def __call__(self, x): return self.c0 + self.c1*x def table(self, L, R, n): """Return a table with n points for L <= x <= R.""" s = '' for x in linspace(L, R, n): y = self(x) s += ' %12g %12g\n ' % (x, y) return s 0.11 A class for parabolas Make a class for evaluating parabolas y = c 0 + c 1 x + c 2 x 2 . Problem: Code: class Parabola : def __init__(self, c0, c1, c2): self.c0, self.c1, self.c2 = c0, c1, c2 def __call__(self, x): return self.c2*x**2 + self.c1*x + self.c0 4
def table(self, L, R, n): """Return a table with n points for L <= x <= R.""" s = '' for x in linspace(L, R, n): y = self(x) s += ' %12g %12g\n ' % (x, y) return s Observation: This is almost the same code as class Line , except for the things with c2 0.12 Class Parabola as a subclass of Line; principles • Parabola code = Line code + a little extra with the c 2 term • Can we utilize class Line code in class Parabola ? • This is what inheritance is about! Writing class Parabola (Line): pass makes Parabola inherit all methods and attributes from Line , so Parabola has attributes c0 and c1 and three methods • Line is a superclass , Parabola is a subclass (parent class, base class; child class, derived class) • Class Parabola must add code to Line ’s constructor (an extra c2 attribute), __call__ (an extra term), but table can be used unaltered • The idea is to reuse as much code in Line as possible and avoid duplicating code 0.13 Class Parabola as a subclass of Line; code A subclass method can call a superclass method in this way: superclass_name.method(self, arg1, arg2, ...) Class Parabola as a subclass of Line : class Parabola (Line): def __init__(self, c0, c1, c2): Line.__init__(self, c0, c1) # Line stores c0, c1 self.c2 = c2 def __call__(self, x): return Line.__call__(self, x) + self.c2*x**2 5
What is gained? • Class Parabola just adds code to the already existing code in class Line - no duplication of storing c0 and c1 , and computing c 0 + c 1 x • Class Parabola also has a table method - it is inherited • __init__ and __call__ are overridden or redefined in the subclass 0.14 We can check class type and class relations with isinstance(obj, type) and issubclass(subclassname, superclassname) >>> from Line_Parabola import Line, Parabola >>> l = Line(-1, 1) >>> isinstance(l, Line) True >>> isinstance(l, Parabola) False >>> p = Parabola(-1, 0, 10) >>> isinstance(p, Parabola) True >>> isinstance(p, Line) True >>> issubclass(Parabola, Line) True >>> issubclass(Line, Parabola) False >>> p.__class__ == Parabola True >>> p.__class__.__name__ # string version of the class name 'Parabola' 0.15 Line as a subclass of Parabola • Subclasses are often special cases of a superclass • A line c 0 + c 1 x is a special case of a parabola c 0 + c 1 x + c 2 x 2 • Can Line be a subclass of Parabola ? • No problem - this is up to the programmer’s choice • Many will prefer this relation between a line and a parabola 6
0.16 Code when Line is a subclass of Parabola class Parabola : def __init__(self, c0, c1, c2): self.c0, self.c1, self.c2 = c0, c1, c2 def __call__(self, x): return self.c2*x**2 + self.c1*x + self.c0 def table(self, L, R, n): """Return a table with n points for L <= x <= R.""" s = '' for x in linspace(L, R, n): y = self(x) s += ' %12g %12g\n ' % (x, y) return s class Line (Parabola): def __init__(self, c0, c1): Parabola.__init__(self, c0, c1, 0) Note: __call__ and table can be reused in class Line ! 0.17 Recall the class for numerical differentiation from Ch. 8 f ′ ( x ) ≈ f ( x + h ) − f ( x ) h class Derivative : def __init__(self, f, h=1E-5): self.f = f self.h = float(h) def __call__(self, x): f, h = self.f, self.h # make short forms return (f(x+h) - f(x))/h def f(x): return exp(-x)*cos(tanh(x)) from math import exp, cos, tanh dfdx = Derivative(f) print dfdx(2.0) 7
0.18 There are numerous formulas numerical differentia- tion f ′ ( x ) = f ( x + h ) − f ( x ) + O ( h ) h f ′ ( x ) = f ( x ) − f ( x − h ) + O ( h ) h f ′ ( x ) = f ( x + h ) − f ( x − h ) + O ( h 2 ) 2 h f ′ ( x ) = 4 f ( x + h ) − f ( x − h ) − 1 f ( x + 2 h ) − f ( x − 2 h ) + O ( h 4 ) 3 2 h 3 4 h f ′ ( x ) = 3 f ( x + h ) − f ( x − h ) − 3 f ( x + 2 h ) − f ( x − 2 h ) + 2 2 h 5 4 h 1 f ( x + 3 h ) − f ( x − 3 h ) + O ( h 6 ) 10 6 h f ′ ( x ) = 1 � − 1 6 f ( x + 2 h ) + f ( x + h ) − 1 2 f ( x ) − 1 � + O ( h 3 ) 3 f ( x − h ) h 0.19 How can we make a module that offers all these for- mulas? It’s easy: class Forward1 : def __init__(self, f, h=1E-5): self.f = f self.h = float(h) def __call__(self, x): f, h = self.f, self.h return (f(x+h) - f(x))/h class Backward1 : def __init__(self, f, h=1E-5): self.f = f self.h = float(h) def __call__(self, x): f, h = self.f, self.h return (f(x) - f(x-h))/h class Central2 : # same constructor # put relevant formula in __call__ 0.20 What is the problem with this type of code? All the constructors are identical so we duplicate a lot of code. 8
Recommend
More recommend