Object Oriented Python - Building Blocks

In this chapter, we will discuss object oriented terms and programming concepts in detail.Class is a just a factory for an instance. This factory contains the blueprint which describes
how to make the instances. An instances or object are constructed from the class. In most cases, we can have more than one instances of a class. Every instance has a set of attribute and these attributes are defined in a class, so every instance of a particular class is expected to have the same attributes.
A class will let you bundle together the behavior and state of an object. Observe the
following diagram for better understanding −
The following points are worth notable when discussing class bundles −
The word behavior is identical to function – it is a piece of code that does something (or implements a behavior)
The word behavior is identical to function – it is a piece of code that does something (or implements a behavior)
The word state is identical to variables – it is a place to store values within a class.
The word state is identical to variables – it is a place to store values within a class.
When we assert a class behavior and state together, it means that a class packages functions and variables.
When we assert a class behavior and state together, it means that a class packages functions and variables.
In Python, creating a method defines a class behavior. The word method is the OOP name given to a function that is defined within a class. To sum up −
Class functions − is synonym for methods
Class functions − is synonym for methods
Class variables − is synonym for name attributes.
Class variables − is synonym for name attributes.
Class − a blueprint for an instance with exact behavior.
Class − a blueprint for an instance with exact behavior.
Object − one of the instances of the class, perform functionality defined in the class.
Object − one of the instances of the class, perform functionality defined in the class.
Type − indicates the class the instance belongs to
Type − indicates the class the instance belongs to
Attribute − Any object value: object.attribute
Attribute − Any object value: object.attribute
Method − a “callable attribute” defined in the class
Method − a “callable attribute” defined in the class
Observe the following piece of code for example −
var = “Hello, John”
print( type (var)) # ‘str’> or <class 'str'>
print(var.upper()) # upper() method is called, HELLO, JOHN
The following code shows how to create our first class and then its instance.
class MyClass(object):
   pass
# Create first instance of MyClass
this_obj = MyClass()
print(this_obj)
# Another instance of MyClass
that_obj = MyClass()
print (that_obj)
Here we have created a class called MyClass and which does not do any task. The argument object in MyClass class involves class inheritance and will be discussed in later chapters. pass in the above code indicates that this block is empty, that is it is an empty class definition.
Let us create an instance this_obj of MyClass() class and print it as shown −
<__main__.MyClass object at 0x03B08E10>
<__main__.MyClass object at 0x0369D390>
Here, we have created an instance of MyClass. The hex code refers to the address where the object is being stored. Another instance is pointing to another address.
Now let us define one variable inside the class MyClass() and get the variable from the instance of that class as shown in the following code −
class MyClass(object):
   var = 9

# Create first instance of MyClass
this_obj = MyClass()
print(this_obj.var)

# Another instance of MyClass

that_obj = MyClass()
print (that_obj.var)
You can observe the following output when you execute the code given above −
9
9

As instance knows from which class it is instantiated, so when requested for an attribute from an instance, the instance looks for the attribute and the class. This is called the attribute lookup.
A function defined in a class is called a method. An instance method requires an instance in order to call it and requires no decorator. When creating an instance method, the first parameter is always self. Though we can call it (self) by any other name, it is recommended to use self, as it is a naming convention.
class MyClass(object):
   var = 9
   def firstM(self):
      print("hello, World")
obj = MyClass()
print(obj.var)
obj.firstM()
You can observe the following output when you execute the code given above −
9
hello, World

Note that in the above program, we defined a method with self as argument. But we cannot call the method as we have not declared any argument to it.
class MyClass(object):
   def firstM(self):
      print("hello, World")
      print(self)
obj = MyClass()
obj.firstM()
print(obj)
You can observe the following output when you execute the code given above −
hello, World
<__main__.MyClass object at 0x036A8E10>
<__main__.MyClass object at 0x036A8E10>

Encapsulation is one of the fundamentals of OOP. OOP enables us to hide the complexity
of the internal working of the object which is advantageous to the developer in the
following ways −
Simplifies and makes it easy to understand to use an object without knowing the internals.
Simplifies and makes it easy to understand to use an object without knowing the internals.
Any change can be easily manageable.
Any change can be easily manageable.
Object-oriented programming relies heavily on encapsulation. The terms encapsulation and abstraction (also called data hiding) are often used as synonyms. They are nearly synonymous, as abstraction is achieved through encapsulation.
Encapsulation provides us the mechanism of restricting the access to some of the object’s
components, this means that the internal representation of an object can’t be seen from outside of the object definition. Access to this data is typically achieved through special
methods − Getters and Setters.
This data is stored in instance attributes and can be manipulated from anywhere outside the class. To secure it, that data should only be accessed using instance methods. Direct access should not be permitted.
class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age

zack = MyClass()
zack.setAge(45)
print(zack.getAge())

zack.setAge("Fourty Five")
print(zack.getAge())
You can observe the following output when you execute the code given above −
45
Fourty Five

The data should be stored only if it is correct and valid, using Exception handling
constructs. As we can see above, there is no restriction on the user input to setAge()
method. It could be a string, a number, or a list. So we need to check onto above code to ensure correctness of being stored.
class MyClass(object):
   def setAge(self, num):
      self.age = num

   def getAge(self):
      return self.age
zack = MyClass()
zack.setAge(45)
print(zack.getAge())
zack.setAge("Fourty Five")
print(zack.getAge())
The __init__ method is implicitly called as soon as an object of a class is instantiated.This will initialize the object.
x = MyClass()
The line of code shown above will create a new instance and assigns this object to the
local variable x.
The instantiation operation, that is calling a class object, creates an empty object. Many classes like to create objects with instances customized to a specific initial state. Therefore, a class may define a special method named ‘ __init__() ‘ as shown −
def __init__(self):
   self.data = []
Python calls __init__ during the instantiation to define an additional attribute that should occur when a class is instantiated that may be setting up some beginning values for that object or running a routine required on instantiation. So in this example, a new, initialized
instance can be obtained by −
x = MyClass()
The __init__() method can have single or multiple arguments for a greater flexibility. The init stands for initialization, as it initializes attributes of the instance. It is called the
constructor of a class.
class myclass(object):
   def __init__(self,aaa, bbb):
      self.a = aaa
      self.b = bbb

x = myclass(4.5, 3)
print(x.a, x.b)
4.5 3

The attribute defined in the class is called “class attributes’ and the attributes defined in the function is called ‘instance attributes’. While defining, these attributes are not prefixed
by self, as these are the property of the class and not of a particular instance.
The class attributes can be accessed by the class itself ( className.attributeName) as well as by the instances of the class (inst.attributeName). So, the instances have access to both the instance attribute as well as class attributes.
>>> class myclass():
   age = 21
>>> myclass.age
21
>>> x = myclass()
>>> x.age
21
>>>
A class attribute can be overridden in an instance, even though it is not a good method to break encapsulation.
There is a lookup path for attributes in Python. The first being the method defined within
the class, and then the class above it.
>>> class myclass(object):
   classy = 'class value'
>>> dd = myclass()
>>> print (dd.classy) # This should return the string 'class value'
class value
>>>
>>> dd.classy = "Instance Value"
>>> print(dd.classy) # Return the string "Instance Value"
Instance Value
>>>
>>> # This will delete the value set for 'dd.classy' in the instance.
>>> del dd.classy
>>> >>> # Since the overriding attribute was deleted, this will print 'class
value'.

>>> print(dd.classy)
class value
>>>
We are overriding the ‘classy’ class attribute in the instance dd. When it’s overridden, the Python interpreter reads the overridden value. But once the new value is deleted with ‘del’, the overridden value is no longer present in the instance, and hence the lookup goes a level above and gets it from the class.
In this section, let us understand how the class data relates to the instance data. We can store data either in a class or in an instance. When we design a class, we decide which data belongs to the instance and which data should be stored into the overall class.
An instance can access the class data. If we create multiple instances, then these instances can access their individual attribute values as well the overall class data.
Thus, a class data is the data that is shared among all the instances. Observe the code given below for better undersanding −
class InstanceCounter(object):
   count = 0 # class attribute, will be accessible to all instances
   def __init__(self, val):
      self.val = val
      InstanceCounter.count +=1 # Increment the value of class attribute, accessible through class name
# In above line, class ('InstanceCounter') act as an object
   def set_val(self, newval):
      self.val = newval

   def get_val(self):
      return self.val

   def get_count(self):
      return InstanceCounter.count
a = InstanceCounter(9)
b = InstanceCounter(18)
c = InstanceCounter(27)

for obj in (a, b, c):
   print ('val of obj: %s' %(obj.get_val())) # Initialized value ( 9, 18, 27)
   print ('count: %s' %(obj.get_count())) # always 3
val of obj: 9
count: 3
val of obj: 18
count: 3
val of obj: 27
count: 3

In short, class attributes are same for all instances of class whereas instance attributes is particular for each instance. For two different instances, we will have two different instance attributes.
class myClass:
   class_attribute = 99

   def class_method(self):
      self.instance_attribute = 'I am instance attribute'

print (myClass.__dict__)
You can observe the following output when you execute the code given above −
{'__module__': '__main__', 'class_attribute': 99, 'class_method': , '__dict__': , '__weakref__': , '__doc__': None}

The instance attribute myClass.__dict__ as shown −
>>> a = myClass()
>>> a.class_method()
>>> print(a.__dict__)
{'instance_attribute': 'I am instance attribute'}

    14 Lectures 
     1.5 hours 

 Harshit Srivastava

    60 Lectures 
     8 hours 

 DigiFisk (Programming Is Fun)

    11 Lectures 
     35 mins

 Sandip Bhattacharya

    21 Lectures 
     2 hours 

 Pranjal Srivastava

    6 Lectures 
     43 mins

 Frahaan Hussain

    49 Lectures 
     4.5 hours 

 Abhilash Nelson
 Print
 Add Notes
 Bookmark this page