Introduction,
Object-oriented Programming, Classes, Class Attributes, Instances, Instance
Attributes, Binding and
Method Invocation, Composition, Sub-classing and Derivation
Inheritance, Built-in
Functions for Classes, Instances, and Other Objects.
================================================
Introduction
to Object-oriented Programming:
Object-oriented
Programming, is a programming paradigm which provides a means of
structuring programs so that properties and behaviors are bundled into
individual objects.
For
example, an object could represent a person with a name property, age, address,
etc., with behaviors like walking, talking, breathing, and running. Or an email
with properties like recipient list, subject, body, etc., and behaviors like
adding attachments and sending.
Classes provide the
definitions of such objects, and instances are realizations of such definitions.
Both are vital components for object-oriented design (OOD), which simply means
to build your system architected in an object-oriented fashion.
One of the most
important reasons to consider working in OOD is that it provides a direct
approach to modeling and solving real-world problems and situations. For
example, let us attempt to model an automobile mechanic shop where you would
take your car in for repair. There are two general entities we would have to
create: humans who interact with and in such a "system," and a
physical location for the activities that define a mechanic shop.
A class called Person
would be created to represent all humans involved in such an activity.
Instances of Person would include the Customer, the Mechanic, and perhaps the Cashier.
Each of these instances would have similar as well as unique behaviors.
For example, all would
have the talk() method as a means of vocal communication as well as a
drive_car() method. Only the Mechanic would have the repair_car() method and
only the Cashier would have a ring_sale() method. The Mechanic will have a
repair_certification attribute while all Persons would have a drivers_license
attribute.
Finally, all of these
instances would be participants in one overseeing class, called the RepairShop,
which would have operating_hours, a data attribute that accesses time
functionality to determine when Customers can bring in their vehicles and when
Employees such as Mechanics and Cashiers show up for work. The RepairShop might
also have a AutoBay class that would have instances such as SmogZone,
TireBrakeZone, and perhaps one called GeneralRepair.
The point of our fictitious RepairShop is to show one
example of how classes and instances plus their behaviors can be used to model
a true-to-life scenario. You can probably also imagine classes such as an
Airport, a Restaurant, a ChipFabPlant, a Hospital, or even a MailOrderMusic
business, all complete with their own participants and functionality.
Classes:
A class is a data structure that
we can use to define objects that hold together data values and behavioral
characteristics. Classes are entities that are the programmatic form of an
abstraction for a real-world problem, and instances are realizations of such
objects. One analogy is to liken classes to blueprints or molds with which to
make real objects (instances).
The
term most likely originates from using classes to identify and categorize
biological families of species to which specific creatures belong and can be
derived into similar yet distinct subclasses. Many of these features apply to
the concept of classes in programming.
In Python, class declarations
are very similar to function declarations, a header line with the appropriate
keyword followed by a suite as its definition, as indicated below:
def functionName(args):
'function
documentation string'
function_suite
class ClassName(object):
'class
documentation string'
class_suite
Both allow you to create
functions within their declaration, closures or inner functions for functions
within functions, and methods for functions defined in classes. The biggest
difference is that you run functions but create objects with classes.
A class is like a Python
container type on steroids. In this section, we will take a close look at
classes and what types of attributes they have. Just remember to keep in mind
that even though classes are objects (everything in Python is an object), they
are not realizations of the objects they are defining.
When you create a class, you are
practically creating your own kind of data type. All instances of that class
are similar, but classes differ from one another. Classes also allow for
derivation. You can create subclasses that are classes but inherit all of the
features and attributes of the "parent" class.
Creating Classes:
Python classes are created using
the class keyword. In the simple form of class declarations, the name of the class immediately follows the keyword:
class ClassName(bases):
class
documentation string'
class_suite
bases is the set of one or more parent classes from which to derive
and class_suite consists of all the component statements, defining class
members, data attributes, and functions. Classes are generally defined at the
top-level of a module so that instances of a class can be created anywhere in a
piece of source code where the class is defined.
The __init__() function:
All
classes have a function called __init__(), which is always executed when the
class is being initiated.
Use the
__init__() function to assign values to object properties, or other operations
that are necessary to do when the object is being created:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person("John", 36)
print(p1.name)
print(p1.age)
def __init__(self, name, age):
self.name = name
self.age = age
p1 = Person("John", 36)
print(p1.name)
print(p1.age)
The self Parameter:
The self parameter is a reference to the current instance of the class,
and is used to access variables that belongs to the class.
Class attributes:
Class attributes belong to the
class itself they will be shared by all the instances. Such attributes are
defined in the class body parts usually at the top.
class sampleclass:
count = 0 #
class attribute
def increase(self):
sampleclass.count += 1
# Calling increase() on an
object
s1 = sampleclass()
s1.increase()
print (s1.count )
# Calling increase on one
more
# object
s2 = sampleclass()
s2.increase()
print (s2.count )
print (sampleclass.count )
Output:
1
2
2
Instance Attributes:
Unlike class attributes,
instance attributes are not shared by objects. Every object has its own copy of
the instance attribute. (In case of class attributes all object refer to single
copy).
To list the attributes of an
instance/object, we have two functions:-
1. vars()- This function
displays the attribute of an instance in the form of an dictionary.
2. dir()- This function displays
more attributes than vars function, as it is not limited to instance. It
displays the class attributes as well.
class sampleclass:
def __init__(self):
self.count = 0
def increase(self):
self.count += 1
# Calling increase on one more object
s1 = sampleclass()
s1.increase()
print (s1.count )
s1.increase()
print (s1.count )
s2 = sampleclass()
s2.increase()
print (s2.count )
>>>
print("Dictionary From:",vars(s1))
Dictionary From: {'count':
3}
>>>
print(dir(s1))
['__class__', '__delattr__',
'__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
'__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', 'count', 'increase']
Difference between Class Attribute and Instance Attribute:
·
An instance attribute is a Python variable
belonging to one, and only one, object. This variable is only accessible in the
scope of this object and it is defined inside the constructor function, __init__(self,..) of
the class.
·
A class attribute is a Python variable
that belongs to a class rather than a particular object. It is shared
between all the objects of this class and it is defined outside the constructor
function, __init__(self,...), of the class.
Binding and Method Invocation:
A method is simply a function
defined as part of a class. This means that methods are class attributes. Methods
can be called only when there is an instance of the class upon which the method
was invoked.
When there is an instance
present, the method is considered bound (to that instance). Without an
instance, a method is considered unbound, and finally, the first argument in
any method definition is the variable self, which represents the instance
object invoking the method.
The variable self is used in
class instance methods to reference the instance to which the method is bound.
Because a method's instance is always passed as the first argument in any
method call, self is the name that was chosen to represent the instance. You
are required to put self in the method declaration (you may have noticed this already)
but do not need to actually use the instance (self) within the method.
If you do not use self in your
method, you might consider creating a regular function instead, unless you have
a particular reason not to. After all, your code, because it does not use the
instance object in any way, "unlinks" its functionality from the
class, making it seem more like a general function.
Invoking Bound Methods:
Methods, whether bound or not,
are made up of the same code. The only difference is whether there is an
instance present so that the method can be invoked. In most cases, you the
programmer will be calling a bound method. Let us say that you have a class
MyClass and an instance of it called mc, and you want to call the MyClass.foo()
method. Since you already have an instance, you can just call the method with
mc.foo(). Recall that self is required to be declared as the first argument in
every method declaration. Well, when you call a bound method, self never needs
to be passed explicitly when you invoke it with an instance. That is your bonus
for being "required" to declare self as the first argument.
The only time when you have to
pass it in is when you do not have an instance and need to call a method
unbound.
Invoking Unbound Methods:
Calling an unbound method happens
less frequently. The main use case for calling a method belonging to a class that you do not have
an instance for is the case where you are deriving a child class and override a
parent method where you need to call the parent's constructor you are
overriding.
Composition:
Example: Class Employee is
container and class Salary is content.
class Salary: #content class
def __init__(self, pay):
self.pay = pay
def get_total(self):
return (self.pay*12)
class Employee: # container class
def __init__(self, pay, bonus):
self.pay = pay
self.bonus = bonus
self.obj_salary = Salary(self.pay)
def annual_salary(self):
return "Total: " +
str(self.obj_salary.get_total() + self.bonus)
obj_emp = Employee(6, 5)
print(obj_emp.annual_salary())
Subclassing, Derivation and Inheritance:
Composition works fine when
classes are distinct and are a required component of larger classes, but when
you want "the same class but with some modification," derivation is a
more logical option.
One of the more powerful aspects
of OOP is the ability to take an already defined class and extend it or make
modifications to it without affecting other pieces of code in the system that
use the currently existing classes.
OOD allows for class features to
be inherited by child classes or subclasses.
These subclasses derive the core
of their attributes from base (super) classes.
In addition, this derivation may
be extended for multiple generations. Classes involved in a one-level
derivation (or that are adjacent vertically in a class tree diagram) have a
parent and child class relationship. Those classes that derive from the same
parent (or that are adjacent horizontally in a class tree diagram) have a
sibling relationship. Parent and all higher-level classes are considered
ancestors.
Creating
Subclasses:
The syntax for creating a
subclass looks just like that for a regular class, a class name followed
by one or more parent classes to inherit from:
classSubClassName
(ParentClass1[, ParentClass2, ...]):
'optional
class documentation string'
class_suite
Example:
class
Person(object):
def __init__(self, name):
self.name = name
def getName(self):
return self.name
def isEmployee(self):
return False
class
Employee(Person):
def isEmployee(self):
return True
emp =
Person("Raju") # An Object of
Person
print(emp.getName(),
emp.isEmployee())
emp =
Employee("Sanju") # An Object of Employee
print(emp.getName(),
emp.isEmployee())
issubclass():
Python provides a function issubclass()
that directly tells us if a class is subclass of another class.
class
Base(object):
pass # Empty Class
class
Derived(Base):
pass # Empty Class
# Driver Code
print(issubclass(Derived,
Base))
print(issubclass(Base,
Derived))
d = Derived()
b = Base()
# b is not an
instance of Derived
print(isinstance(b,
Derived))
# But d is an
instance of Base
print(isinstance(d,
Base))
In Python, there are two types of Inheritance:
- Multiple Inheritance
- Multilevel Inheritance
Multiple inheritance:
Python supports multiple
inheritance. We specify all parent classes as comma separated list in bracket.
Multiple Inheritance means that
you're inheriting the property of multiple classes into one. In case you have
two classes, say A and B, and you want to create a new class which inherits the
properties of both A and B, then:
class A:
# variable of class A
# functions of class A
class B:
# variable of class A
# functions of class A
class C(A, B):
# class C inheriting property of both class
A and B
# add more properties to class C
So just like a child inherits
characteristics from both mother and father, in python, we can inherit multiple
classes in a single child class.
Example:
class Base1(object):
class Base1(object):
def __init__(self):
self.name1 =
"Raju"
print ("From Base1
i am Raju ")
class
Base2(object):
def __init__(self):
self.name2 =
"Sanju"
print ("From Base2
i am Sanju")
class
Derived(Base1, Base2):
def __init__(self):
# Calling constructors
of Base1 and Base2 classes
Base1.__init__(self)
Base2.__init__(self)
print
("Derived")
def printStrs(self):
print(self.name1,
self.name2)
ob = Derived()
ob.printStrs()
Multilevel
Inheritance:
In multilevel inheritance, we inherit the classes at
multiple separate levels. We have three classes A, B and C, where A is the
super class, B is its sub(child) class and C is the sub class of B.
class A:
# properties of class A
class B(A):
# class B inheriting property of class A
# more properties of class B
class C(B):
# class C inheriting property of class B
# thus, class C also inherits properties of
class A
# more properties of class C
Accessing parent members in a subclass:
We can access
parent members in a subclass using two methods
1. Using
Parent class name
2.
Using
function super()
Using Parent class name:
Base Class members can be accessed in derived class using
base class name.
class
Base(object):
def __init__(self, x):
self.x = x
class
Derived(Base):
def __init__(self, x, y):
Base.x = x
self.y = y
def printXY(self):
print(Base.x, self.y)
d = Derived(10,
20)
d.printXY()
Using function super():
Base class members can be accessed in
derived class using super().
class
Base(object):
def __init__(self, x):
self.x = x
class
Derived(Base):
def __init__(self, x, y):
super(Derived, self).__init__(x)
self.y = y
def printXY(self):
# Here Base.x won't work here because
super() is used in constructor
print(self.x, self.y)
d = Derived(10,
20)
d.printXY()
Built-in Functions for Classes, Instances, and Other Objects:
issubclass():
The issubclass() function returns True if the specified
object is a subclass of the specified object, otherwise False.
Syntax:
issubclass(object,
subclass)
Example:Check if the class myObj is a subclass of myAge:
class myAge:
age = 36
class myObj(myAge):
name = "John"
age = myAge
x = issubclass(myObj,
myAge)
isinstance():
The isinstance() function checks
if the object (first argument) is an instance or subclass of classinfo class
(second argument).
Syntax:
isinstance(object, class)
Example:
class myObj:
name = "John"
y = myObj()
x = isinstance(y, myObj)
hasattr():
The hasattr() function returns True if the specified
object has the specified attribute, otherwise False.
Syntax:
hasattr(object,
attribute)
Example: Check if the "Person" object has
the "age" property:
class Person:
name = "John"
age = 36
country = "Norway"
x =
hasattr(Person, 'age')
getattr():
The getattr() function returns the value of the
specified attribute from the specified object.
Syntax:
getattr(object,
attribute, default)
Example: Use the "default" parameter to
write a message when the attribute does not exist:
class Person:
name = "John"
age = 36
country = "Norway"
x =
getattr(Person, 'page', 'my message')
setattr():
The setattr() function sets the value of the
specified attribute of the specified object.
Syntax:
setattr(object,
attribute, value)
Example :Change the value of the "age"
property of the "person" object:
class Person:
name = "John"
age = 36
country = "Norway"
setattr(Person,
'age', 40)
delattr():
The delattr() function will delete the specified
attribute from the specified object.
Syntax:
delattr(object,
attribute)
Example: Delete the "age" property from
the "person" object:
class Person:
name = "John"
age = 36
country = "Norway"
delattr(Person,
'age')
dir():
The dir() function returns all properties and
methods of the specified object, without the values.
This function will return all the properties and
methods, even built-in properties which are default for all object.
Syntax:
dir(object)
Example: Display the content of an object:
class Person:
name = "John"
age = 36
country = "Norway"
print(dir(Person))
super():
The super() function is used to give access to
methods and properties of a parent or sibling class.
The super() function returns an object that
represents the parent class.
Syntax
super()
Example: Create a class that will inherit all the
methods and properties from another class:
class Parent:
def __init__(self, txt):
self.message = txt
def printmessage(self):
print(self.message)
class
Child(Parent):
def __init__(self, txt):
super().__init__(txt)
x =
Child("Hello, and welcome!")
x.printmessage()
vars():
The vars() function returns the
__dic__ attribute of an object.
The __dict__ attribute is a
dictionary containing the object's changeable attributes.
Calling the vars() function
without parameters will return a dictionary containing the local symbol table.
Syntax
vars(object)
Example: Return the __dict__
atribute of an object called Person:
class Person:
name = "John"
age = 36
country = "norway"
x = vars(Person)