(U)Unclassified

Object Oriented Programming

OOP Overview

  • Modules
    • Module Structure and use
    • Module Import
  • Packages
    • Package Structure
    • Useful Commands

Overview (Cont.)

  • Python Classes
    • Keywords
    • Creating A Class
    • __init__
    • Public, Protected, Private
    • Compostition vs Inheritance
    • Function vs Methods
  • Exception Handling
  • OOP Principles
  • OOP Terminology Review

Objectives

Object Oriented Programming Objectives

Modules

Modules

  • Modules are reusable code that can be imported into other scripts or programs
  • Modules are single files in either .py, .pyc or .pyo format
  • When a module is imported, code that is not wrapped in a function is executed and the functions themselves are added to the namespace... allowing them to be called upon

Module Structure

							
								#/usr/lib/python/site-packages/triangle.py
			
								divisor_count_to_find = 500
								
								def triangle_number(n):
									return n*(n+1)/2
								
								def divisors(tn):
									counter = 0
									n_sqrt = int(pow(tn, 0.5))
									for i in range(1, n_sqrt + 1):
										if tn%i == 0:
											counter += 2
									if n_sqrt * n_sqrt == tn:
										counter -= 1
									return counter
								
								def start():
									start_number = 1
									div_numbers = 0
									while (div_numbers < divisor_count_to_find):
										tn = triangle_number(start_number)
										div_numbers = divisors(tn)
										start_number += 1
									print(div_numbers)
									print(tn)
								
								if __name__ == '__main__':
									start()
								
							
						

Using Modules

  • First, we import the module. We will learn more coming up.
  • After the module is imported we can begin utilizing the module's objects.
  • When a module is imported normally, the module will be on it's own namespace.
    • Thus we need to use dot notation to access the modules objects

Importing Modules

  • The most common way is to import the module is using the import keyword alone
  • These modules are imported from the same dir as your program
  • This method prevents collisions by putting the modules objects into it's own namespace
							
								# Most common, clean way to do it. Prevents collisions. 
								import triangle
								triangle.method # Call method
							
						

There are other methods as well. We can give our modules aliases

							
								# Same as above except we gave triangle an alias
								import triangle as tri
								tri.method
							
						

We can also import specific modules without throwing them into a namespace.

							
								from triangle import divisor_count_to_find, triangle_number

								print divisor_count_to_find # output 500
								triangle_number(5)								
							
						

We can take the previous example a step further and implement aliases. This will allow us to change the names up to prevent collisons.

							
								from triangle import divisor_count_to_find as dcf, triangle_number as tn

								print dcf # output 500
								tn(5)
							
						

Lastly, we can also import an entire module without a namespace. The * tells Python to import all modules without a namespace. This is a dangerous method and should only be used if you are sure there will not be collisions.

							
								from triangle import *
							
						

Packages

  • Packages are a way to structure Python's module namespace
  • A package is just a collection of modules
  • A module named __init__.py is required to treat the directory as a package

Package Structure

							
								/                       # Top directory
								math_stuff/         # Package
									__init__.py     # special file
									fib.py          # fib module 
									triangle.py     # triangle module 
									Calculator.py   # calculator class module
								more_math_stuff/    # additional package 
									__init__.py 
									other_modules.py						
							
						

How to Import From a Package

So let's take the package below and walk through an import.

							
								/                       # Top directory
								math_stuff/         # Package
									__init__.py     # special file
									fib.py          # fib module 
									triangle.py     # triangle module 
									Calculator.py   # calculator class module
								more_math_stuff/    # additional package 
									__init__.py 
									other_modules.py # import and run this file (module)													
							
						

There are two ways we can go about this:

							
								import more_math_stuff.other_modules

								more_math_stuff.other_modules.do_something()
																				
							
						

OR

							
								from more_math_stuff import other_modules as om

								om.do_something()							
							
						

Useful Commands

							
        # dir() Returns list of names in current local scope or with argument, 
        # attempts to return a list of valid attributes for object
								>>> print dir()
								['__builtins__', '__doc__', '__name__', '__package__']
								>>> import(struct)
								>>> print dir()
								['__builtins__', '__doc__', '__name__', '__package__', 'struct']
								>>> print dir(struct)
								['Struct', '__builtins__', '__doc__', '__file__', '__name__', '__package__', '_clearcache', 'calcsize', 'error', 'pack', 'pack_into', 'unpack', 'unpack_from']
								
								# help() Invokes built-in help system
								print help(struct)
								# Try it
								print help()
								# Try it													
							
						

Packages Labs

Lab 5a

User Classes

  • Classes are used to create objects that bundle data and functionality together
  • Classes and built-in types were at one point different

Keywords

Reserved words of the language, and cannot be used as ordinary identifiers. Do not use any of these for any naming. They must be spelled exactly as written here:

							
								and       del       from      not       while    
								as        elif      global    or        with     
								assert    else      if        pass      yield    
								break     except    import    print     
								class     exec      in        raise              
								continue  finally   is        return             
								def       for       lambda    try																					
							
						

To verify that a string is a keyword you can use keyword.iskeyword; to get the list of reserved keywords you can use keyword.kwlist:

							
								>>> import keyword
								>>> keyword.iskeyword('break')
								True
								>>> keyword.kwlist
								['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 
								 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 
								 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 
								 'while', 'with', 'yield']																												
							
						

Creating a Class

							
								# This is the class
								class Person:
									"""Stage 1 of Person Class
								
									We will expand on this."""
								
									firstName = "James"
									lastName = "Bond"
									birthday = "08/21/1800"
									age = "200"
									email = "[email protected]"
								
									def f(self):
										return "This is the Person class"
								
								# This is the instantiation of class Person as object x
								x = Person()
								
								# This is using object x
								print x.f()
								print "This person's name is: {} {}".format(x.firstName, x.lastName)
								
								# We can also do this...
								print Person.firstName																											
							
						
							
								>>> class someClass:
								...     x = 100
								...
								>>> someClass.x
								100
								>>> someInstance = someClass()
								>>> someInstance.x
								100
								>>> someClass.x = 400
								>>> someInstance.x
								400
								>>> someInstance.x = 1000
								>>> someInstance.x
								1000
								>>> someClass.x = 0
								>>> someInstance.x
								1000																																	
							
						

__init__()

  • It is possible to create objects with instances customized to a specific state.
    • Just like C++, we have the ability to construct objects.
    • But we also have the ability to customize objects.
  • __init__() is not a constructor.
    • Instead, this comes after the constructor and allows us to customize our object.
  • __new__() is the constructor.

self

  • Before __init__(), comes the word self.
    • The self in python represents or points the instance which it was called.
							
								>>> class someClass:
								...     ex1 = 0
								...     ex2 = 0
								...
								...     def f(self, val):
								...             ex1 = val
								...             self.ex2 = val
								...
								>>> # the class has been created. We also created a method.
								... # the method attempts to change ex1 and self.ex2 to val
								...
								>>> someInstance = someClass()
								>>> someInstance.ex1
								0
								>>> someInstance.ex2
								0
								>>> someInstance.f(100)
								>>> someInstance.ex1
								0
								>>> someInstance.ex2
								100																														
							
						

Utilizing __init()__

Now that we have a general understanding of self, let's create a __init__() method.

We can do something as simple as:

							
								class X:

									def __init__(self):
										self.i = "yay"
							
								# Invalid
								print X.i  # We will get an error, i is not a class variable
								
								# Valid
								a = X()
								print a.i  # Output will be 'yay'																																			
							
						

This prevents us from being able to change data across all classes. But we have even more flexibility than that in Python, we can also pass additional arguments to __init__() via on object instantiation.

							
								def __init__(self, firstName, lastName):
									self.firstName = firstName
									self.lastName = lastName


								# Once we go to create the object
								x = Person("James", "Bond")
																																		
							
						

Now that we have learned how to utilize __init__()... let's update our example.

							
								# This is the class
								class Person:
									"""Stage 2 of Person Class
								
									We will expand on this."""
								
									def __init__(self, firstName, lastName, birthday, age, email):
										self.firstName = firstName
										self.lastName = lastName
										self.birthday = birthday
										self.age = age
										self.email = email
								
									def printDetails(self):
										print "First Name: {}".format(self.firstName)
										print "First Name: {}".format(self.lastName)
										print "First Name: {}".format(self.birthday)
										print "First Name: {}".format(self.age)
										print "First Name: {}".format(self.email)
								
								# This is the instantiation of class Person as object x
								x = Person("James", "Bond", "08/21/1800", 200, "[email protected]")
								x.printDetails()																												
							
						

getters and setters

  • Using getters and setters is a safety method used for better data encapsulation
  • A getter is a method that gets a value from private attributes within a class
  • A setter is a method that sets a private attribute within a class
							
								class Example: 
									def __init__(self, age = 0): 
										self._age = age 
									
									# getter method 
									def get_age(self): 
										return self._age 
									
									# setter method 
									def set_age(self, x): 
										self._age = x 
								
								a = Example() 
								
								# setting the age using setter 
								a.set_age(57) 
								
								# retrieving age using getter 
								print(a.get_age()) 
								
								print(a._age) 
							
						
							
								Output:
								57
								57
							
						

User CLasses Labs

Lab 5b Lab 5c

Exceptions

Python exceptions allow you to attempt code that may result in an error and execute additional functionality on said error.

  • Try/except/finally/else
    • Try statement begins an exception handling block
  • Raise
    • Triggers exceptions
  • Except
    • Handles the exception
  • Multiple Exceptions
    • Handles multiple exceptions

Exception Example

							
								try:
									<statements%gt;
								except: <name%gt;:
									<statements%gt;
								except: <name%gt; as <data%gt;:
									<statements%gt;
								else:
									<statements%gt;
								
								try:
									<statements%gt;
								finally:
									<statements%gt;																																			
							
						

Exception Example 2

							
								"""
								Checks a certain range of numbers to see if they can divide into a user specified num
								"""
								# Program main, runs at start of program
								def launch():
									num = input('What number would you like to check?')
									amount = input('How many numbers do you want to check?')
								
									if isInt(num) == False or isInt(amount) == False:
										print("You must enter an integer")
										launch() 
									elif int(amount) < 0 or int(num) < 0:
										print("You must enter a number greater than 0")
										launch() 
									else:
										divisible_by(int(num), int(amount))
								
								# Checks num divisible numbers up to amount or itself
								def divisible_by(num, amount):
									i = 1.0
									while (num / i >= 1 and amount > 0):
										if num % i == 0:
											print('{} is divisible by {}'.format(int(num), int(i)))
											amount -= 1
										i += 1
								
								# EXCEPTION HANDLING
								def isInt(x):
									try:
										int(x) ###
										return True
									except ValueError:
										return False
								
								launch()
																																										
							
						

Exception Example 3

							
								class CustomError(Exception):
									def _init_(self, msg):
										self.msg = msg
										
									def _str_(self):
										return "your error is {}".format(self.msg)
										
									def doStuff(danger):
										if danger == True:
											raise CustomError("Whoa don't do that!")
											print("Success") #What happens here?
								
								try:
									doStuff(True)
									
								except CustomError as stuff:
									print(stuff)																									
							
						

Labs

TODO("Labs")

OOP Principles

Object Oriented:

Programming typified by a data-centered (as opposed to a function-centered) approach to program design.

The Zen of Python

							
								Beautiful is better than ugly.
								Explicit is better than implicit.
								Simple is better than complex.
								Complex is better than complicated.
								Flat is better than nested.
								Sparse is better than dense.
								Readability counts.
								Special cases aren't special enough to break the rules.
								Although practicality beats purity.
								Errors should never pass silently.
								Unless explicitly silenced.
								In the face of ambiguity, refuse the temptation to guess.
								There should be one-- and preferably only one --obvious way to do it.
								Although that way may not be obvious at first unless you're Dutch.
								Now is better than never.
								Although never is often better than
								right
								now.
								If the implementation is hard to explain, it's a bad idea.
								If the implementation is easy to explain, it may be a good idea.
								Namespaces are one honking great idea -- let's do more of those!
								—Tim Peters																																			
							
						

OOP Terminology Review

Object Oriented:

Attribute

  • Values associated with an individual object. Attributes are accessed using the 'dot syntax':
							
								a.x means fetch the x attribute from the 'a' object.																																		
							
						

Argument

A value passed to a function (or method) when calling the function. There are two types of arguments:

  • Keyword Argument
  • Positional Argument

Keyword Argument

  • An argument preceded by an identifier (e.g. name=) in a function call or passed as a value in a dictionary preceded by **.
							
								complex(real=3, imag=5)
								complex(**{'real': 3, 'imag': 5})																							
							
						

Positional Argument

  • An argument that is not a keyword argument. Positional arguments can appear at the beginning of an argument list and/or be passed as elements of an iterable preceded by *. For example, 3 and 5 are both positional arguments in the following calls:
							
								complex(3, 5)
								complex(*(3, 5))																														
							
						

Class

  • A template for creating user-defined objects. Class definitions normally contain method definitions that operate on instances of the class.

Class Variable

  • Variables defined within the class definition that belong to the class. These variables are shared to all instances of the class.
							
								class TestClass:
									i = 10
								
								print TestClass.i																	
							
						

Constructors

  • Fuctions that are automatically called when you create a new instance of a class.

Deconstructor

  • Called when an object gets destroyed. It’s the polar opposite of the constructor, which gets called on creation. These methods are only called on creation and destruction of the object. They are not called manually but completely automatic.

Function/Method Overloading

  • Function Overloading is the ability to create multiple functions with the same name. This allows us to pass different parameters. In Python, this is generally not needed. Instead... we can set default and optional parameters.
							
								def someFunc(x, y, z = None):
									if z is None:
										# Set a default for z or don't use it
									else:
										# Set and use all three
								
								someFunc(1,2)
								someFunc(1,2,3)
																													  
							
						

Generator

  • A function which returns an iterator. It looks like a normal function except that it contains yield statements for producing a series of values usable in a for-loop or that can be retrieved one at a time with the next() function.

Immutable

  • An object with fixed value. Immutable objects include numbers, strings and tuples.

Inheritance

  • A term used in OOP meaning that classes can inherit from other classes. In other words, it's the transfer of characteristics from one class to another class/classes that are derived from it.

Instance Variable

  • Similar to Class Variables... except they are only accessible to that one instance. These generally start with the 'self' keyword and are contained within methods.
							
								class NameClass:
									def __init__(self, name):
										self.name = name
								
								dis_class =  NameClass('your_name')
								print dis_class.name
								#OUTPUT:
								'dis_name'													  
							
						

Object

  • Any data with state (attributes or value, etc...) and defined behavior (methods or functions, etc...). An object can be assigned to a variable or passed into a function as a argument. It's a unique data structure that's defined by it's type or class, etc... Everything in Python is an object.
  • Instance: An individual object of a certain class.
  • Instantiation: Instantiation is the act of creating an object instance from a class.

Method

  • Methods are functions that are called using the attribute notation. There are two flavors: built-in methods (such as append() on lists) and class instance methods. A method is similar to a function but is associated with an object.

Self

  • Represents the instance of the class. By using the "self" keyword we can access the attributes and methods of the class in python.

Polymorphism

  • The ability to leverage the same interface for different underlying forms such as data typesclasses or functions.
  • Polymorphism is a way of making a function accept objects of different classes if they behave similarly.
							
								len("hello")                    # returns 5 as result
								len([1,2,3,4,45,345,23,42])     # returns 8 as result
							
						

Polymorphism Exaple:

							
								class Square:
									side = 5     
									def calculate_area(self):
										return self.side * self.side

								class Triangle:
									base = 5
									height = 4
									def calculate_area(self):
										return 0.5 * self.base * self.height

								sq = Square()
								tri = Triangle()
								print("Area of square: ", sq.calculate_area())
								print("Area of triangle: ", tri.calculate_area())
							
						
							
								Area of square: 25
								Area of triangle: 10.0
							
						

Special Method

  • A method that is called implicitly by Python to execute a certain operation on a type, such as addition. Such methods have names starting and ending with double underscores. Special methods are documented in Special method names.

OOP Review Lab

Lab 5d

Questions?

Summary

  • Modules
  • Packages
  • User Classes
  • Composition vs Inheritance
  • Exception Handling
  • Object Oriented Programming Principles/Terminology