COMPSCI 107 Computer Science Fundamentals

Slides:



Advertisements
Similar presentations
Lecture 04 – Classes.  Python has a number of classes built-in  lists, dictionaries, sets, int, float, boolean, strings  We can define our own classes.
Advertisements

Abstract Data Type Fraction Example
Classes 2 COMPSCI 105 SS 2015 Principles of Computer Science.
COMPSCI 105 S Principles of Computer Science 12 Abstract Data Type.
Lecture 05 – Classes.  A class provides the definition for the type of an object  Classes can store information in variables  Classes can provide methods.
Classes 2 COMPSCI 105 S Principles of Computer Science.
Unit 8 Classes and Objects; Inheritance Special thanks to Roy McElmurry, John Kurkowski, Scott Shawcroft, Ryan Tucker, Paul Beck for their work. Except.
Classes 3 COMPSCI 105 S Principles of Computer Science.
Chapter 11 Introduction to Classes Intro to Computer Science CS1510, Section 2 Dr. Sarah Diesburg.
Programming in Java Unit 2. Class and variable declaration A class is best thought of as a template from which objects are created. You can create many.
Classes 1 COMPSCI 105 S Principles of Computer Science.
CSC 142 Computer Science II Zhen Jiang West Chester University
Chapter 10 Defining Classes. The Internal Structure of Classes and Objects Object – collection of data and operations, in which the data can be accessed.
Chapter 4 Introduction to Classes, Objects, Methods and strings
Overview The Basics – Python classes and objects Procedural vs OO Programming Entity modelling Operations / methods Program flow OOP Concepts and user-defined.
1 Programming for Engineers in Python Autumn Lecture 6: More Object Oriented Programming.
Programming with Java © 2002 The McGraw-Hill Companies, Inc. All rights reserved. 1 McGraw-Hill/Irwin Chapter 5 Creating Classes.
Object Oriented Programing (OOP)
Object-Oriented Programming © 2013 Goodrich, Tamassia, Goldwasser1Object-Oriented Programming.
Classes COMPSCI 105 SS 2015 Principles of Computer Science.
Lecture 09 – Classes.  At the end of this lecture, students should be able to:  Define a new class  Store state information about instances of the.
More about Java Classes Writing your own Java Classes More about constructors and creating objects.
Topic 8Classes, Objects and Methods 1 Topic 8 l Class and Method Definitions l Information Hiding and Encapsulation l Objects and Reference Classes, Objects,
Topic 1 Object Oriented Programming. 1-2 Objectives To review the concepts and terminology of object-oriented programming To discuss some features of.
Attribute - CIS 1068 Program Design and Abstraction Zhen Jiang CIS Dept. Temple University SERC 347, Main Campus 12/24/2016.
Adapted from slides by Marty Stepp and Stuart Reges
CMSC201 Computer Science I for Majors Lecture 25 – Classes
Classes (Part 1) Lecture 3
Topics Designing a Program Input, Processing, and Output
Object Oriented Programming
Building Java Programs
Classes and Objects; Inheritance
Classes and Objects – digging deeper
COMPSCI 107 Computer Science Fundamentals
Classes and OOP.
CMSC201 Computer Science I for Majors Lecture 22 – Binary (and More)
Mathematical operators overload
Topics Procedural and Object-Oriented Programming Classes
Topic 7 Introduction to Classes and Objects
Java Programming: Guided Learning with Early Objects
Yanal Alahmad Java Workshop Yanal Alahmad
CS-104 Final Exam Review Victor Norman.
Chapter 3: Using Methods, Classes, and Objects
Variables, Expressions, and IO
CSC240 Computer Science III
Subroutines Idea: useful code can be saved and re-used, with different data values Example: Our function to find the largest element of an array might.
Introduction to Classes
Inheritance Basics Programming with Inheritance
IFS410: Advanced Analysis and Design
Teach A-Level Computer Science: Object-Oriented Programming in Python
Week 8 Classes and Objects
Adapted from slides by Marty Stepp and Stuart Reges
Computer Programming with JAVA
Python Primer 1: Types and Operators
15-110: Principles of Computing
COP 3330 Object-oriented Programming in C++
Topics Designing a Program Input, Processing, and Output
Topics Designing a Program Input, Processing, and Output
Object Oriented Programming in java
CIS 199 Final Review.
Designing a class.
Defining Classes and Methods
Lecture 18 Python OOP.
A Level Computer Science Topic 6: Introducing OOP
Lecture 8 Object Oriented Programming (OOP)
More Basics of Python Common types of data we will work with
Classes and Objects Systems Programming.
Defining Functions.
Presentation transcript:

COMPSCI 107 Computer Science Fundamentals Lecture 08 – Classes

Learning outcomes At the end of this lecture, students should be able to: Define a new class Store state information about instances of the class Define new methods of the class Override the default behaviour for standard operations COMPSCI 107 - Computer Science Fundamentals

Why classes? Managing Complexity

Motivation Separates high-level constructs from implementation details Reduces complexity

Motivation Separates high-level constructs from implementation details Reduces complexity client code class definition p = Polynomial() p.add_term(5, 2) p.add_term(1, 1) p.add_term(7, 0) q = Polynomial() q.add_term(10, -2) result = p.add(q) print(result) print(result.evaluate(2)) class Polynomial: ....

Motivation Separates high-level constructs from implementation details Modular code client code class definition p = Polynomial() p.add_term(5, 2) p.add_term(1, 1) p.add_term(7, 0) q = Polynomial() q.add_term(10, -2) result = p.add(q) print(result) print(result.evaluate(2)) class Polynomial: .... contract / interface

Motivation Separates high-level constructs from implementation details Modular code client code class definition p = Polynomial() p.add_term(5, 2) p.add_term(1, 1) p.add_term(7, 0) q = Polynomial() q.add_term(10, -2) result = p.add(q) print(result) print(result.evaluate(2)) class Polynomial: .... contract / interface

Motivation Separates high-level constructs from implementation details Modular code client code class definition p = Polynomial() p.add_term(5, 2) p.add_term(1, 1) p.add_term(7, 0) q = Polynomial() q.add_term(10, -2) result = p.add(q) print(result) print(result.evaluate(2)) class Polynomial: .... contract / interface

Object-oriented programming Here are two objects (from the “real world”): They are both the same kind of thing (car) class So they both have the same kinds of attributes state They can also “do” the same kinds of actions behaviour

Object-oriented programming And here are two objects of different kinds: In object-oriented programming, we try to model the problem we are solving using objects. We can have many objects of various kinds (classes). At any point in time, each object in our program has some particular state (a set of variables storing data for that object). We can perform actions on the objects in our program by calling methods on them.

(e.g. accelerate, brake, refuel, ...) Classes vs. objects Blueprint Building new cars State State Shared behaviour (e.g. accelerate, brake, refuel, ...)

(e.g. accelerate, brake, refuel, ...) Classes vs. objects Blueprint Building new cars State State Shared behaviour (e.g. accelerate, brake, refuel, ...) class Car: .... Car class

(e.g. accelerate, brake, refuel, ...) Classes vs. objects Blueprint Building new cars State State Shared behaviour (e.g. accelerate, brake, refuel, ...) a = Car() b = Car() class Car: .... Creating new objects Instantiating objects Car class

Classes vs. objects Blueprint Building new cars State State Shared behaviour (e.g. accelerate, brake, refuel, ...) a = Car() b = Car() class Car: .... state state a Creating new objects Instantiating objects Car class b Car objects Instances of the Car class

Classes Python has a number of classes built-in list, dict, int, float, bool, str,.... a = [10, 20, 30] b = {'a': 5, 'b': 10} c = 10 d = 10.55 e = True f = 'Hello' print(type(a)) print(type(b)) print(type(c)) print(type(d)) print(type(e)) print(type(f)) <class 'list'> <class 'dict'> <class 'int'> <class 'float'> <class 'bool'> <class 'str'>

Classes We can define our own classes Classes consist of This allows us to create new types of objects in Python Think of a class definition as a blueprint that can be used to create many objects of the same type Classes consist of State variables (sometimes called instance variables) Methods (functions that are linked to a particular instance of the class) class Name_of_class: definition goes here

The simplest class possible We can define a class with an “empty” definition Can’t be truly empty – the statement “pass” is a statement that does nothing “pass” is often used as a placeholder during code development class Point: pass p = Point() print(p) p.x = 10 p.y = 20 print(p.x, p.y) <__main__.Point object at 0x026A7410> 10 20

Saving the class Classes are designed to help build modular code Classes can be defined within a module that also contains application code, or they can be defined in separate files Multiple classes can be defined in the same file In this course, we will typically store each class in their own module To use a class in another module, you will need to import the module Geometry.py main.py class Point: pass from Geometry import Point p = Point()

The object in memory Visualise objects as enclosing a set of “attributes” Attributes are just like variables, but they are associated with the object main.py from Geometry import Point p = Point() p.x = 5 p.y = 7 x 5 7 y p

Initialising the state of the object We may want to define the Point class such that when we create an object we also set the initial values of the attributes main.py from Geometry import Point p = Point(5, 7) To allow this, we need to define a special method of the Point class called a initialiser. The initialiser method is called whenever you create a Point object.

Initialisers Each class should contain a initialiser method x 5 p y 7 The name of the method is __init__ The method always has at least one input parameter, called “self” “self” is a reference to the object that we are creating The constructor method can have other parameters main.py Geometry.py from Geometry import Point p = Point(5, 7) print(p.x) print(p.y) class Point: def __init__(self, loc_x, loc_y): self.x = loc_x self.y = loc_y x 5 p y 7

Adding functionality to the class Defining more methods For example, we could add a method to our Point class to shift a point by a given amount in horizontal and vertical directions The method is named normally, but has the additional parameter “self” as the very first parameter All methods that are called on an object need the “self” parameter main.py Geometry.py from Geometry import Point p = Point(2, 3) print(p.x, p.y) p.translate(1, 0) class Point: def __init__(self, loc_x, loc_y): self.x = loc_x self.y = loc_y def translate(self, dx, dy): self.x += dx self.y += dy We call these methods using the name of the object (p) followed by a dot (.) followed by the name of the method (translate)

Adding functionality to the class main.py Geometry.py from Geometry import Point p = Point(2, 3) print(p.x, p.y) p.translate(1, 0) class Point: def __init__(self, loc_x, loc_y): self.x = loc_x self.y = loc_y def translate(self, dx, dy): self.x += dx self.y += dy x 2 p y 3

Adding functionality to the class main.py Geometry.py from Geometry import Point p = Point(2, 3) print(p.x, p.y) p.translate(1, 0) class Point: def __init__(self, loc_x, loc_y): self.x = loc_x self.y = loc_y def translate(self, dx, dy): self.x += dx self.y += dy x 3 p y 3

How does this work? p.translate(1, 0) translate(p, 1, 0) When you call a method like this: the method call translates to: p.translate(1, 0) translate(p, 1, 0) And this first parameter is the magical “self”!

Exercise Define a Square class with an initializer that accepts a number representing the length of the side of the square. Add a method to the class to calculate the perimeter of the square. The following code shows how the class may be used >>> from Geometry import Square >>> s = Square(10) >>> p = s.perimeter() >>> print(p) 40 COMPSCI 107 - Computer Science Fundamentals

Summary A class provides the definition for a certain kind of object Classes define the variables that an object will use to store information Classes define the methods that we can use to perform actions with objects Example: main.py Geometry.py from Geometry import Square side = 10 s = Square(side) class Square: def __init__(self, s): self.size = s

Example: a Fraction class Consider writing a class to represent a fraction in Python Create a fraction Add, subtract, multiply, divide two fractions Display a text representation of a fraction numerator 1 / 2 denominator

Example: a Fraction class class Fraction: pass f = Fraction() f.num = 1 f.den = 2 print('{} / {}'.format(f.num, f.den))

Example: a Fraction class Placing the definition of the Fraction class in a separate file helps to “abstract away” the details of the class implementation from the client code Fraction.py main.py class Fraction: pass from Fraction import Fraction f = Fraction()

Example Fraction object (aka “instance” of the Fraction class) f

Example 1 num den 2 f Fraction object (aka “instance” of the Fraction class) num 1 den 2 f

Visualising Fraction objects We could create several Fraction objects as follows: f1 = Fraction(1, 2) f2 = Fraction(3, 4) f3 = Fraction(7, 8) 1 numerator 2 denominator f1 3 numerator f2 4 denominator f3 7 numerator 8 denominator

Initialiser for the Fraction class All classes must have an initialiser The initialiser for the Fraction class should store the numerator and the denominator Fraction.py class Fraction: def __init__(self, top, bottom): self.numerator = top self.denominator = bottom

Using the Fraction class So far we can create a Fraction object: We can access the state variables directly Although it is considered not good practice to do so What else can we do with Fraction objects? Nothing yet... we need to write the methods! main.py from Fraction import Fraction f1 = Fraction(3, 4) main.py print(f1.numerator) print(f1.denominator)

Using the Fraction class Why is accessing the state variables directly not advised? from Fraction import Fraction f1 = Fraction(3, 4) print(f1.numerator) print(f1.denominator) f1.denominator = 0 This shouldn’t be allowed – but currently we can’t prevent people using our Fraction class like this. What can we do?

Hiding the instance variables To prevent direct modification of the data fields (instance variables), we can stop the client (the user of the class) from accessing them directly This is known as data hiding, which can be done by defining private data fields in a class In Python, the private data fields are defined with two leading underscore characters You can also define private methods in the same way (although this is not something we will do) class Fraction: def __init__(self, top, bottom): self.__numerator = top self.__denominator = bottom

Accessing private data fields Private data fields can be accessed by code within the class definition, but they cannot be accessed by code outside the class (client code) To make a private data field accessible to the client, provide a method to return its value To enable a private data field to be modifiable, provide a method to set its value f1 = Fraction(3, 4) print(f1.__numerator) Traceback (most recent call last): File "lecture.py", line 5, in <module> print(f1.__numerator) AttributeError: 'Fraction' object has no attribute '__numerator'

Accessor and mutator methods A “get” method is referred to as an accessor method A “set” method is referred to as a mutator method def get_numerator(self): return self.__numerator def set_numerator(self, top): self.__numerator = top Example: accessing the private data fields through accessor and mutator methods from Fraction import Fraction f1 = Fraction(1, 2) print(f1.get_numerator()) f1.set_numerator(12) 1 12

Overriding default behaviour All classes get a number of special methods provided by default including methods for creating a text-based representation of the object and methods for comparing two objects of the class type But these default versions are not very useful. We should define our own that make more sense for our class. f1 = Fraction(1, 2) f2 = Fraction(1, 2) print(f1) print(f1 == f2) Calls a special function “__str__” <Fraction.Fraction object at 0x025E7410> False Calls a special function “__eq__”

The __str__ method You should define a __str__ method in your class This should return a “nicely-formatted” version of the object class Fraction: def __init__(self, top, bottom): self.__numerator = top self.__denominator = bottom .... def __str__(self): return '(' + str(self.__numerator) + '/' + str(self.__denominator) + ')' f1 = Fraction(1, 2) print(f1) (1/2)

The __eq__ method The __eq__ method will be called automatically whenever you use “==” to compare two instances of your class the default behaviour just compares the references class Fraction: def __init__(self, top, bottom): self.__numerator = top self.__denominator = bottom def __eq__(self, other): return self.__numerator * other.__denominator == other.__numerator * self.__denominator f1 = Fraction(1, 2) f2 = Fraction(3, 4) f3 = Fraction(3, 6) print(f1 == f2) print(f1 == f3) False True

The __repr__ method There is one more special method that you should define The __repr__ method should return “a string that unambiguously describes the object” Ideally, the representation should be an expression that could be used to create the object class Fraction: def __init__(self, top, bottom): self.__numerator = top self.__denominator = bottom .... def __repr__(self): return 'Fraction(' + str(self.__numerator) + ', ' + str(self.__denominator) + ')' If you don’t define __str__ then by default it will call __repr__, but otherwise __str__ is used for printing. In this example, we call __repr__ explicitly using repr() f1 = Fraction(1, 2) print(repr(f1)) Fraction(1, 2)

Overloading operators Python operators work for built-in classes But the same operator behaves differently with different types For example, consider “+” Performs arithmetic between two numbers Merges two lists Concatenates two strings Operator overloading is when one operator can perform different functions depending on the context

The __add__ method Another special method is __add__ (5/6) This is called automatically when the “+” operator is used In other words, f1 + f2 gets translated into f1.__add__(f2) class Fraction: def __init__(self, top, bottom): self.__numerator = top self.__denominator = bottom def __add__(self, other): new_num = self.__numerator * other.__denominator + self.__denominator * other.__numerator new_den = self.__denominator * other.__denominator return Fraction(new_num, new_den) f1 = Fraction(1, 2) f2 = Fraction(1, 3) result = f1 + f2 print(result) (5/6)

Improving the __eq__ method Recall the __eq__ method in our Fraction class: What happens here? def __eq__(self, other): return self.__numerator * other.__denominator == other.__numerator * self.__denominator x = Fraction(2, 3) y = Fraction(1, 3) z = y + y print(x == z) print(x is z) w = x + y print(w == 1)

Improving the __eq__ method Recall the __eq__ method in our Fraction class: What happens here? def __eq__(self, other): return self.__numerator * other.__denominator == other.__numerator * self.__denominator x = Fraction(2, 3) y = Fraction(1, 3) z = y + y print(x == z) print(x is z) w = x + y print(w == 1)

Improving the __eq__ method Recall the __eq__ method in our Fraction class: What happens here? def __eq__(self, other): return self.__numerator * other.__denominator == other.__numerator * self.__denominator x = Fraction(2, 3) y = Fraction(1, 3) z = y + y print(x == z) print(x is z) w = x + y print(w == 1) True False Traceback (most recent call last): File "lecture.py", line 13, in <module> print(w == 1) File "Fraction.py", line 19, in __eq__ return self.__numerator * ... AttributeError: 'int' object has no attribute '_Fraction__denominator'

Improving the __eq__ method To fix this, we need to check if the type of “other” is a Fraction object But maybe we would like to be able to compare Fraction objects to integers? we could extend this so that if the type of “other” is an int, then we perform an appropriate comparison. def __eq__(self, other): if not isinstance(other, Fraction): return False return self.__numerator * other.__denominator == other.__numerator * self.__denominator

Simplifying fractions There is no need to store numerators or denominators larger than required Simplifying fractions: calculate the greatest common divisor(GCD) of the numerator and denominator (i.e. the common divisors of 12 and 30 are 1, 2, 3 and 6) divide both numerator and denominator by GCD 12 / 30 is the same as 2 / 5

Calculating the GCD Fortunately, Euclid came up with a nice algorithm for computing this around 300BC Given two number, n and m, find the number k such that k is the largest number the evenly divides both n and m: Notice also that there is no “self” as the first input to this method. We will not be calling this method on an object – this is an example of a static method (it is not called on an object of the class) class Fraction: .... def __gcd(m, n): while m % n != 0: old_m = m old_n = n m = old_n n = old_m % old_n return n By defining this method with a leading “__”, it makes it private to the Fraction class. Client code (that uses the Fraction class) cannot directly call the gcd method (it is only used by the Fraction class itself to reduce fractions)

Improving the constructor Now we can improve the constructor so that it always represents a fraction using the “lowest terms” (reduced) form: class Fraction: def __init__(self, top, bottom): common = Fraction.__gcd(top, bottom) self.__numerator = top // common self.__denominator = bottom // common Notice how static methods are called using the name of the class (and not a particular instance of the class)

Improving the constructor Example from Fraction import Fraction x = Fraction(2,3) y = Fraction(3,4) z = x + y print(z) w = z + Fraction(5, 12) print(w) print(w + Fraction(1,6) == Fraction(2,1)) (17/12) (11/6) True

Other standard Python operators Many standard operators and funtions: https://docs.python.org/3/library/operator.html Common Arithmetic operators object.__add__(self, other) object.__sub__(self, other) object.__mul__(self, other) object.__floordiv__(self, other) object.__truediv__(self, other) Common Relational operators object.__lt__(self, other) object.__le__(self, other) object.__eq__(self, other) object.__ne__(self, other) object.__gt__(self, other) object.__ge__(self, other)

Summary A class is a template, a blueprint and a data type for objects A class defines the data fields of objects, and provides an initialiser for initialising objects and other methods for manipulating data The initialiser is always called __init__() The first input parameter is always “self”, and this refers to the object being constructed The data fields in the class should be hidden to prevent data tampering and to make the class easy to maintain This is achieved by prefixing the field name with __ We can override the default methods in a class definition Such as __repr__ and __str__ and __eq__