Programming objects - Python in easy steps (2014)

Python in easy steps (2014)

7

Programming objects

This chapter demonstrates how to use Python for Object Oriented Programming.

Encapsulating data

Creating instance objects

Addressing class attributes

Examining built-in attributes

Collecting garbage

Inheriting features

Overriding base methods

Harnessing polymorphism

Summary

Encapsulating data

A “class” is a specified prototype describing a set of properties that characterize an object. Each class has a data structure that can contain both functions and variables to characterize the object.

The properties of a class are referred to as its data “members”. Class function members are known as its “methods”, and class variable members (declared within a class structure but outside any method definitions) are known as its “attributes”.

image

Class members can be referenced throughout a program using dot notation, suffixing the member name after the class name, with syntax of class-name.method-name() or class-name.attribute-name.

A class declaration begins with the class keyword, followed by a programmer-specified name (adhering to the usual Python naming conventions but beginning in uppercase) then a : colon. Next come indented statements optionally specifying a class document string, class variable attribute declarations, and class method definitions - so the class block syntax looks like this:

class ClassName :

‘‘ class-documentation-string ‘‘‘

class-variable-declarations

class-method-definitions

The class declaration, which specifies its attributes and methods, is a blueprint from which working copies (“instances”) can be made.

All variables declared within method definitions are known as “instance” variables and are only available locally within the method in which they are declared - they cannot be directly referenced outside the class structure.

Typically, instance variables contain data passed by the caller when an instance copy of the class is created. As this data is only available locally for internal use it is effectively hidden from the rest of the program. This technique of data “encapsulation” ensures that data is securely stored within the class structure and is the first principle of Object Oriented Programming (OOP).

image

It is conventional to begin class names with an uppercase character and object names with lowercase.

All properties of a class are referenced internally by the dot notation prefix self - so an attribute named “sound” is self.sound. Additionally, all method definitions in a class must have self as their first argument - so a method named “talk” is talk( self ).

When a class instance is created, a special __init__( self ) method is automatically called. Subsequent arguments can be added in its parentheses if values are to be passed to initialize its attributes.

image

The class documentation string can be accessed via the special__doc__ docstring attribute with Classname.__doc__ .

A complete Python class declaration could look like this example:

class Critter :

‘‘‘ A base class for all critter properties. ‘‘‘

count = 0

def __init__( self , chat ) :

self.sound = chat

Critter.count += 1

def talk( self ) :

return self.sound

It is useful to examine the class components of this example:

The variable count is a class variable whose integer value gets shared among all instances of this class - this value can be referenced as Critter.count from inside or outside the class

The first method __init__() is the initialization method that is automatically called when an instance of the class is created

The __init__() method in this case initializes an instance sound, with a value passed from the chat argument, and increments the value of the count class variable whenever an instance of this class is created

The second method talk() is declared like a regular function except the first argument is self which is automatically incorporated - no value needs to be passed from the caller

The talk() method in this case simply returns the value encapsulated in the sound instance variable

image

While a program class cannot perfectly emulate a real-word object, the aim is to encapsulate all relevant attributes and actions.

Creating instance objects

An “instance” of a class object is simply a copy of the prototype created by calling that class name’s constructor and specifying the required number of arguments within its parentheses. The call’s arguments must match those specified by the __init__() method definition - other than a value for the internal self argument.

The class instance object returned by the constructor is assigned to a variable using the syntax instance-name = ClassName( args ).

Dot notation can be used to reference the methods and class variable attributes of an instance object by suffixing their name as instance-name.method-name() or instance-name.attribute-name.

image

A constructor creates an instance of a class and is simply the class name followed by parentheses containing any required argument values.

Typically, a base class can be defined as a Python module file so it can be imported into other scripts where instance objects can be easily created from the “master” class prototype.

imageStart a new Python script by declaring a new class with a descriptive document string

class Bird :

‘‘’A base class to define bird properties.’’’

image

Bird.py

imageNext, add an indented statement to declare and initialize a class variable attribute with an integer zero value

count = 0

imageNow, define the intializer class method to initialize an instance variable and to increment the class variable

def __init__( self , chat ) :

self.sound = chat

Bird.count += 1

imageFinally, add a class method to return the value of the instance variable when called - then save this class file

def talk( self ) :

return self.sound

image

You must not pass an argument value for the self argument as this is automatically incorporated by Python.

imageStart another Python script by making features of the class file available, then display its document string

from Bird import *

print( ‘\nClass Instances Of:\n’ , Bird.__doc__ )

image

instance.py

imageNext, add a statement to create an instance of the class and pass a string argument value to its instance variable

polly = Bird( ‘Squawk, squawk!’ )

image

Bird instance - polly

imageNow, display this instance variable value and call the class method to display the common class variable value

print( ‘\nNumber Of Birds:’ , polly.count )

print( ‘Polly Says:’ , polly.talk() )

imageCreate a second instance of the class, passing a different string argument value to its instance variable

harry = Bird( ‘Tweet, tweet!’ )

image

Bird instance - harry

imageFinally, display this instance variable value and call the class method to display the common class variable value

print( ‘\nNumber Of Birds:’ , harry.count )

print( ‘Harry Says:’ , harry.talk() )

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see two instances of the Bird class get created

image

image

The class variable count can also be referenced with Bird.count but the encapsulated instance variable sound can only be accessed by calling an instance’s talk() method.

Addressing class attributes

An attribute of a class instance can be added, modified, or removed at any time using dot notation to address the attribute. Making a statement that assigns a value to an attribute will update the value contained within an existing attribute or create a new attribute of the specified name containing the assigned value:

instance-name.attribute-name = value

del instance-name.attribute-name

Alternatively, you can use the following Python built-in functions to add, modify, or remove an instance variable:

getattr( instance-name , ‘attribute-name’ ) - return the attribute value of the class instance

hasattr( instance-name , ‘attribute-name’ ) - return True if the attribute value exists in the instance, otherwise return False

setattr( instance-name , ‘attribute-name’ , value ) - update the existing attribute value or create a new attribute in the instance

delattr( instance-name , ‘attribute-name’ ) - remove the attribute from the instance

image

The attribute name specified to these built-in functions must be enclosed within quotes.

The name of attributes automatically supplied by Python always begin with an underscore character to notionally indicate “privacy” - so these should not be modified, or removed. You can add your own attributes named in this way to indicate privacy if you wish but in reality these can be modified like any other attribute.

imageStart a new Python script by by making features of the Bird class available that was created here

from Bird import *

image

address.py

imageNext, create an instance of the class then add a new attribute with an assigned value using dot notation

chick = Bird( ‘Cheep, cheep!’ )

chick.age = ‘1 week’

imageNow, display the values in both instance variable attributes

print( ‘\nChick Says:’ , chick.talk() )

print( ‘Chick Age:’ , chick.age )

imageThen, modify the new attribute using dot notation and display its new value

chick.age = ‘2 weeks’

print( ‘Chick Now:’ , chick.age )

image

Bird instance - chick

imageNext, modify the new attribute once more, this time using a built-in function

setattr( chick , ‘age’ , ‘3 weeks’ )

imageNow, display a list of all non-private instance attributes and their respective values using a built-in function

print( ‘\nChick Attributes...’ )

for attrib in dir( chick ) :

if attrib[0] != ‘_’ :

print( attrib , ‘:’ , getattr( chick , attrib ) )

imageFinally, remove the new attribute and confirm its removal using a built-in functions

delattr( chick , ‘age’ )

print( ‘\nChick age Attribute?’ , hasattr( chick , ‘age’ ) )

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see the instance attributes get addressed

image

image

This loop skips any attribute whose name begins with an underscore so “private” attributes will not get displayed in the list.

Examining built-in attributes

Each Python class is automatically created with a number of built-in private attributes whose values can be referenced using dot notation. For example, with class-name.__doc__ to see the document string attribute value of a specified class name.

The built-in dir() function can be used to display a list of all the built-in attributes in a class specified within its parentheses by testing whether each attribute name begins with an underscore.

The built-in __dict__ attribute contains a “namespace” dictionary of class component keys and their associated values. The dictionary of a base class includes its default __init__() method, and all class methods and attributes. The dictionary of a class instance includes its instance attributes.

imageStart a new Python script by making features of the Bird class available that was created here

from Bird import *

image

builtin.py

imageNext, add a statement to create an instance of the class

zola = Bird( ‘Beep, beep!’ )

imageNow, add a loop to display all built-in instance attributes

print( ‘\nBuilt-in Instance Attributes...’ )

for attrib in dir( zola ) :

if attrib[0] == ‘_’ :

print( attrib )

imageThen, add a loop to display all items in the class dictionary

print( ‘\nClass Dictionary...’ )

for item in Bird.__dict__ :

print( item , ‘:’ , Bird.__dict__[ item ] )

imageFinally, add a loop to display all items in the instance dictionary

print( ‘\nInstance Dictionary...’ )

for item in zola.__dict__ :

print( item , ‘:’ , zola.__dict__[ item ] )

image

The function values stored in the dictionary are the machine addresses where the functions are stored.

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to examine the built-in attributes

image

image

Bird - zola.

The class dictionary output displays all class attributes, whereas the instance dictionary output displays only instance attributes - the class attributes are shared by the instance.

image

A class instance is first created in this program so the __init__() method has been called to increment the count value before the dictionary gets listed.

image

The __weakref__ attribute is simply used internally for automatic garbage collection of “weak references” in the program for efficiency.

Collecting garbage

When a class instance object is created it is allocated a unique memory address that can be seen using the built-in id() function. Python automatically performs “garbage collection” to free up memory space by periodically deleting un-needed objects such as class instances - so their memory address becomes vacant.

Whenever an object gets assigned a new name or gets placed in a container, such as a list, its “reference count” increases. Conversely, whenever these are removed or go out of scope its count decreases. The object becomes eligible for collection when this count is zero.

Destroying an instance of a class may, optionally, call upon a “destructor” to execute a __del__() method - explicitly reclaiming occupied memory space and executing any specified statements.

imageStart a new Python script by declaring a class with an initializer method creating two instance variables and a method to display one of those variable values

class Songbird :

def __init__( self , name , song ) :

self.name = name

self.song = song

print( self.name , ‘Is Born...’ )

image

Songbird.py

imageNext, add a method to simply display both variable values

def sing( self ) :

print( self.name , ‘Sings:’ , self.song )

imageNow, add a destructor method for confirmation when instances of the class are destroyed - then save this file

def __del__( self ) :

print( self.name , ‘Flew Away!\n’ )

imageStart another Python script by making features of the class file available

from Songbird import *

image

garbage.py

imageNext, create an instance of the class then display its instance attribute values and its identity address

bird_1 = Songbird( ‘Koko’ , ‘Tweet, tweet!\n’ )

print( bird_1.name , ‘ID:’ , id( bird_1 ) )

bird_1.sing()

imageNow, delete this instance - calling its destructor method

del bird_1

image

Songbird - Koko

imageCreate two more instances of the class then display their instance attribute values and their identity addresses

bird_2 = Songbird( ‘Louie’ , ‘Chirp, chirp!\n’ )

print( bird_2.name , ‘ID:’ , id( bird_2 ) )

bird_2.sing()

bird_3 = Songbird( ‘Misty’ , ‘Squawk, squawk!\n’ )

print( bird_3.name , ‘ID:’ , id( bird_3 ) )

bird_3.sing()

image

Songbird - Louie

imageFinally, delete these instances - calling their destructors

del bird_2

del bird_3

image

Songbird - Misty

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see memory space handled by garbage collection

image

image

The second instance created here is allocated the memory address vacated when the first instance was deleted.

Inheriting features

A Python class can be created as a brand new class, like those in previous examples, or can be “derived” from an existing class. Importantly, a derived class inherits members of the parent (base) class from which it is derived - in addition to its own members.

image

The ability to inherit members from a base class allows derived classes to be created that share certain common properties, which have been defined in the base class. For example, a “Polygon” base class may define width and height properties that are common to all polygons. Classes of “Rectangle” and Triangle” could be derived from the Polygon class - inheriting width and height properties, in addition to their own members defining their unique features.

image

The virtue of inheritance is extremely powerful and is the second principle of Object Oriented Programming (OOP).

A derived class declaration adds ( ) parentheses after its class name specifying the name of its parent base class.

imageCreate a new Python script that declares a base class with two class variables and a method to set their values

class Polygon :

width = 0

height = 0

def set_values( self , width , height ) :

Polygon.width = width

Polygon.height = height

image

Polygon.py

imageNext, create a script that declares a derived class with a method to return manipulated class variable values

from Polygon import *

class Rectangle( Polygon ) :

def area( self ) :

return self.width * self.height

image

Rectangle.py

imageNow, create another script that declares a derived class with a method to return manipulated class variable values

from Polygon import *

class Triangle( Polygon ) :

def area( self ) :

return ( self.width * self.height ) / 2

image

Triangle.py

imageSave the three class files then start a new Python script by making features of both derived classes available

from Rectangle import *

from Triangle import *

image

inherit.py

imageNext, create an instance of each derived class

rect = Rectangle()

trey = Triangle()

imageNow, call the class method inherited from the base class, passing arguments to assign to the class variables

rect.set_values( 4 , 5 )

trey.set_values( 4 , 5 )

imageFinally, display the result of manipulating the class variables inherited from the base class

print( ‘Rectangle Area:’ , rect.area() )

print( ‘Triangle Area:’ , trey.area() )

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see output get displayed using inherited features

image

image

A class declaration can derive from more than one class by listing multiple base classes in the parentheses after its name in the declaration.

image

Don’t confuse class instances and derived classes - an instance is a copy of a class, whereas a derived class is a new class that inherits properties of the base class from which it is derived.

Overriding base methods

A method can be declared in a derived class to override a matching method in the base class - if both method declarations have the same name and the same number of listed arguments. This effectively hides the base class method as it becomes inaccessible unless it is called explicitly, using the base class name for identification.

Where a method in a base class supplies a default argument value this can be used in an explicit call to the base method or alternative values can be supplied by overriding methods.

imageCreate a new Python script that declares a base class with an initializer method to set an instance variable and a second method to display that variable value

class Person :

‘‘’A base class to define Person properties.’’’

def __init__( self , name ) :

self.name = name

def speak( self , msg = ‘(Calling The Base Class)’ ) :

print( self.name , msg )

image

Person.py

imageNext, create a script that declares a derived class with a method that overrides the second base class method

from Person import *

‘‘’A derived class to define Man properties.’’’

class Man( Person ) :

def speak( self , msg ) :

print( self.name , ‘:\n\tHello!’ , msg )

image

Man.py

imageNow, create another script that also declares a derived class with a method that once again overrides the same method in the base class

from Person import *

‘‘’A derived class to define Hombre properties.’’’

class Hombre( Person ) :

def speak( self , msg ) :

print( self.name , ‘:\n\tHola!’ , msg )

image

Hombre.py

imageSave the three class files then start a new Python script by making features of both derived classes available

from Man import *

from Hombre import *

image

override.py

imageNext, create an instance of each derived class, initializing the “name” instance variable attribute

guy_1 = Man( ‘Richard’ )

guy_2 = Hombre( ‘Ricardo’ )

imageNow, call the overriding methods of each derived class, assigning different values to the “msg” argument

guy_1.speak( ‘It\’s a beautiful evening.\n’ )

guy_2.speak( ‘Es una tarde hermosa.\n’ )

image

Man -Richard

Hombre - Ricardo

imageFinally, explicitly call the base class method, passing a reference to each derived class - but none for the “msg” variable so its default value will be used

Person.speak( guy_1 )

Person.speak( guy_2 )

imageSave the file in your scripts directory then open a Command Prompt window there and run this program - to see output from overriding and base class methods

image

image

The method declaration in the derived class must exactly match that in the base class to override it.

Harnessing polymorphism

The three cornerstones of Object Oriented Programming (OOP) are encapsulation, inheritance, and polymorphism. Examples earlier in this chapter have demonstrated how data can be encapsulated within a Python class, and how derived classes inherit the properties of their base class. This example introduces the final cornerstone principle of polymorphism.

image

The term “polymorphism” (from Greek, meaning “many forms” ) describes the ability to assign a different meaning, or purpose, to an entity according to its context.

In Python, the + character entity can be described as polymorphic because it represents either the arithmetical addition operator, in the context of numerical operands, or the string concatenation operator in the context of character operands.

Perhaps more importantly, Python class methods can also be polymorphic because the Python language uses “duck typing” - meaning... if it walks like a duck, swims like a duck, and quacks like a duck, then that bird is reckoned to be a duck.

In a duck-typed language you can create a function to take an object of any type and call that object’s methods. If the object does indeed have the called methods (is reckoned to be a duck) they are executed, otherwise the function signals a run-time error.

Like-named methods of multiple classes can be created and instances of those classes will execute the associated version.

imageCreate a new Python script that declares a class with methods to display strings unique to the class

class Duck :

def talk( self ) :

print( ‘\nDuck Says: Quack!’ )

def coat( self ) :

print( ‘Duck Wears: Feathers’ )

image

Duck.py

imageNext, create a Python script that declares a class with like-named methods but to display strings unique to this class

class Mouse :

def talk( self ) :

print( ‘\nMouse Says: Squeak!’ )

def coat( self ) :

print( ‘Mouse Wears: Fur’ )

image

Mouse.py

imageSave the two class files then start a new Python script by making features of both classes available

from Duck import *

from Mouse import *

image

polymorph.py

imageNext, define a function that accepts any single object as its argument and attempts to call methods of that object

def describe( object ) :

object.talk()

object.coat()

imageNow, create an instance object of each class

donald = Duck()

mickey = Mouse()

image

Duck - donald

imageFinally, add statements to call the function and pass each instance object to it as an argument

describe( donald )

describe( mickey )

image

Mouse - mickey

imageSave the file in your scripts directory and open a Command Prompt window there then run this program - to see the methods of associated versions get called

image

image

A class can have only one method with a given name - method overloading is not supported in Python.

Object Oriented Programming with Python allows data encapsulation, inheritance, and polymorphism. Base class methods can be overridden by like-named methods in derived classes. Python does not, however, support the technique of “overloading” found in other languages - in which methods of the same name can be created with different argument lists in a single class.

Summary

A class is a data structure prototype describing object properties with its methods and attribute members

Each class declaration begins with the class keyword and is followed by an indented code block that may contain a class document string, class variables, and class methods

Class variables have global scope but instance variables (declared within method definitions) have only local scope

Instance variables encapsulate data securely in a class structure and are initialized when a class instance is created

Properties of a class are referenced by dot notation and are addressed internally using the self prefix

A class instance is a copy of the prototype that automatically calls its __init__() method when the instance is first created

An attribute of a class can be added, modified, or removed using dot notation or manipulated using the built-in functions getattr(), hasattr(), setattr(), and delattr()

The name of attributes automatically supplied by Python begin with an underscore character to notionally indicate privacy

The built-in __dict__ attribute contains a namespace dictionary of class component keys and values

Python automatically performs garbage collection but the del keyword can remove objects and call the class destructor

A derived class inherits the method and attribute members of the parent base class from which it is derived

A method of a derived class can override a matching method of the same name in its parent base class

Python is a duck-typed language that supports polymorphism for like-named methods of multiple classes