Object Oriented programming in Python
The first paradigm of programming was:
"Programs are active, data is static,
data sits there in a lump and programs DO things to or with the data".
As programs got more complex, larger, it got harder to handle all the details. There were problems caused by the complexity of team programming, problems of controlling access to the data, controlling the formatting of data, preventing the duplication of code.
The paradigm of Object Oriented programming is:
"Data is not static, data knows things and knows how to DO things.
Data and code would be bundled together into one thing, an object".
What an object "knows" are its properties or attributes (its values).
What it can do are its methods.
Objects _sometimes_ refer to things in the real world:
a customer, a student, a class, a widget, a circle, a square,
a shopping cart, etc.
Sometimes they do not, e.g., objects can be a transaction.
Class Examples (in UML notation)
Some basic principles of OOP:
A class is a framework or template that you write describing some kind of concept you want to treat as one thing, e.g., a customer, a student, a UPC, a temperature.
Most people do not write classes to represent something very simple, like one integer or a string. You write a class to 'bundle together' some values which belong to the same thing. (This is not the only reason but it's the first one people think of.) A fraction has two parts, a numerator and a denominator. You could certainly keep those in two different variables but there would be nothing to mark them as "together" except your treatment of them. This "name" variable and "address" variable and GPA variable and major variable all belong to a student, but that is not enforced by the language. Putting them into an object allows you to talk about them all as one thing. Printing them, comparing them, inputting, outputting, whatever else you need to do with them, can be accomplished via the object.
NOTE: OOP has not become standardized enough for anyone to make many statements which would be true for ALL languages. C++ and Python are both OOP languages, but they still do things in different ways. Sometimes they even use different terms, not for most of the basic things, but some specialized areas. Tread carefully when switching languages and writing OOP!
you create a Class by describing the data in it,
making a method to set up one (constructor) and
making more methods (functions) to work with it.
Then in the program you ask for the creation of
as many as you need, by calling the constructor with
the information needed to make one of the class.
You use other methods you have provided to do things with the objects.
Syntax:
To start the definition of a class, you use the keyword class and an
identifier followed by a colon. Typically this is at the left margin
and near the top of the file.
Everything indented under this line is part of the class definition.
class identifier:
To make a constructor in Python, use the name __init__ (that's two underscores on both sides) and a parameter called self. You may also have other parameters to provide initial values. Self is a special name which means that things (variables, other functions) that are inside the object can be accessed. You do this by using the notation "self." in front of the object's pieces.
def __init__ (self, horz, vert): self.x = horz self.y = vertThis could be a constructor for a Point class, so that when you make a Point, you have to provide the horizontal and vertical location of the Point. You say, but the Point constructor is called with TWO arguments, not three! That is true, the "self" argument is implied.
Another strange fact: you call the constructor by the name of the _class_, even though its name inside the class definition is __init__! Constructors do not return any value. A constructor is called ONE time per object, at the time it is created.
Another piece of vocabulary: when you call the constructor in a program,
you are asking for the instantiation of the class or an "instance" of the class.
This thing you
are asking to be created is also called an object.
(Yes, it looks like a variable! that's how we have treated them all
semester long.)
A complete example should help:
class Fraction: # note, no parentheses! this is not a function! ## the data in this class represents a fraction, two integers ## for numerator (num) and denimnator (den) ##This is the constructor def __init__ (self, numerator, denminator): self.num = numerator if denminator != 0: # data validation, no 0 denominators! self.den = denminator else: self.den = 1 # a default value of 1, it may not be what the user wants # but it's a lot better than having a zero! ## that's it - you do not have to return anything! ## this is how the object's data can be turned into a string ## usually used for outputting it def __str__ (self): return str(self.num) + "/" + str(self.den) ## Get the greatest common divisor and divide both parts of ## the fraction by it to reduce to lowest terms. ## Returns a new object containing the reduced fraction. ## Assumes we have included the gcd function written in the ## C++ vs. Python notes. ## def reduce (self): g = gcd(self.num, self.den) return Fraction (self.num//g, self.den//g) ## why // and not /? / will always be float and we want ints ## // will give an int result if both operands are ints ## these are called accessors, they just access and return one ## of the values of the object (also called getters) ## why bother with these? Python lets you have direct access to ## an object's values anyway. It's style. In C++ it's REQUIRED! def get_num(self): return self.num def get_den(self): return self.den def main(): n1 = int(input("Enter a numerator for fraction 1 ")) d1 = int(input("Enter a denominator for fraction 1 ")) n2 = int(input("Enter a numerator for fraction 2 ")) d2 = int(input("Enter a denominator for fraction 2 ")) frac1 = Fraction(n1, d1) # calling the constructor (aka __init__) frac2 = Fraction(n2, d2) # calling the constructor (aka __init__) print(frac1, "is fraction 1") # using the __str__ method given above print("fraction 2 is", frac2) print(frac1.reduce()) # note, no argument! self is implied frac3 = frac2.reduce() print(frac3) myden = frac3.get_den() if myden == 1: print("frac3 is a whole number!")