0% found this document useful (0 votes)
1 views41 pages

Python Unit4 FileHandling Classes&Objects Notes

File Handling python

Uploaded by

gowthamdhruvi71
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
1 views41 pages

Python Unit4 FileHandling Classes&Objects Notes

File Handling python

Uploaded by

gowthamdhruvi71
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Inheritance is defined as the mechanism of inheriting the properties of the base class to the

child class.

Types of Inheritance in Python


Types of Inheritance depend upon the number of child and parent classes involved. There are
four types of inheritance in Python:

Single Inheritance:
Single inheritance enables a derived class to inherit properties from a single parent class, thus
enabling code reusability and the addition of new features to existing code.

Example:
Python program to demonstrate
# single inheritance

# Base class
class Parent:
def func1(self):
print("This function is in parent class.")

# Derived class

class Child(Parent):
def func2(self):
print("This function is in child class.")

# Driver's code
object = Child()
object.func1()
object.func2()

When a class can be derived from more than one base class this type of inheritance is called
multiple inheritances. In multiple inheritances, all the features of the base classes are inherited
into the derived class.

class Mother:
mothername = ""
def mother(self):
print([Link])

# Base class2

class Father:
fathername = ""

def father(self):
print([Link])

# Derived class

class Son(Mother, Father):


def parents(self):
print("Father :", [Link])
print("Mother :", [Link])

# Driver's code
s1 = Son()
[Link] = "RAM"
[Link] = "SITA"
[Link]()

Multilevel Inheritance :
In multilevel inheritance, features of the base class and the derived class are further inherited
into the new derived class. This is similar to a relationship representing a child and a
grandfather.
# Python program to demonstrate
# multilevel inheritance

# Base class
class Grandfather:
def __init__(self, grandfathername):
[Link] = grandfathername

# Intermediate class
class Father(Grandfather):
def __init__(self, fathername, grandfathername):
[Link] = fathername

# invoking constructor of Grandfather class


Grandfather.__init__(self, grandfathername)

# Derived class
class Son(Father):
def __init__(self, sonname, fathername, grandfathername):
[Link] = sonname

# invoking constructor of Father class


Father.__init__(self, fathername, grandfathername)

def print_name(self):
print('Grandfather name :', [Link])
print("Father name :", [Link])
print("Son namee :", [Link])
# Driver code
s1 = Son('Prince', 'Rampal', 'Lal mani')
print([Link])
s1.print_name()

Output:
Lal mani
Grandfather name : Lal mani
Father name : Rampal
Son name : Prince

Multipath Inheritance
When a class is derived from two or more classes which are derived from the same base class
then such type of inheritance is called multipath inheritance.

Here class 'D' derived from classes 'B' and 'C', which are derived from same base class 'A'.
Multipath inheritance, also known as diamond inheritance or the diamond problem, is a scenario
that occurs in object-oriented programming when a class inherits from two or more classes that
have a common base class. This can lead to ambiguity and potential issues in the way method
calls and attribute resolution are handled.

Python, like many object-oriented programming languages, supports multiple inheritance, which
allows a class to inherit attributes and methods from multiple parent classes. However, when
multiple inheritance is involved and there's a common ancestor, it can lead to complications in
method resolution and attribute access.

class A:
def some_method(self):
print("Method from class A")

class B(A):
def some_method(self):
print("Method from class B")
class C(A):
def some_method(self):
print("Method from class C")
class D(B, C):
pass
obj = D()
obj.some_method()

In the above example, class D inherits from both classes B and C, both of which also inherit from
class A.
This forms a diamond-like inheritance structure:

/\

B C

\/

When you create an instance of class D and call the some_method() on it, which version of the
method should be invoked? Should it be the one from class B, the one from class C, or the one
from class A?

Python uses a method resolution order (MRO) algorithm called the C3 Linearization algorithm to
determine the order in which the base classes are searched for methods and attributes.

In the example above, the MRO of class D would be [D, B, C, A], meaning that methods and
attributes are looked up first in class D, then in class B, then in class C, and finally in class A.

So, when you call obj.some_method(), Python will execute the method from class B, because it's
the first one in the MRO that defines the method.

In Python, access specifiers are not enforced in the same way as in some other programming
languages like C++ or Java. Access specifiers are used to control the visibility and accessibility
of class attributes and methods within the context of object-oriented programming. However,
Python takes a different approach, emphasizing developer responsibility and convention over
strict enforcement.

There are three "levels" of access specifiers commonly used in other languages:

1. Public: Attributes and methods are accessible from anywhere, both within and outside the class.
2. Protected: Attributes and methods are meant to be used within the class and its subclasses. By
convention, a single underscore (_) prefix is used to indicate a protected attribute/method, but it
doesn't actually restrict access in any way.
3. Private: Attributes and methods are intended to be used only within the class that defines them.
By convention, a double underscore (__) prefix is used to indicate a private attribute/method.
However, it doesn't truly prevent access, but instead performs a name mangling, making it more
difficult to access these members from outside the class.

class MyClass:
def __init__(self):
self.public_var = 1 # Public attribute
self._protected_var = 2 # Protected attribute
self.__private_var = 3 # Private attribute

def public_method(self):
print("This is a public method")

def _protected_method(self):
print("This is a protected method")

def __private_method(self):
print("This is a private method")

obj = MyClass()

# Accessing public attributes/methods


print(obj.public_var)
obj.public_method()

# Accessing protected attributes/methods (not restricted, just a convention)


print(obj._protected_var)
obj._protected_method()

# Accessing private attributes/methods (name mangling applied)


# This doesn't prevent access, but it makes it less straightforward
print(obj._MyClass__private_var)
obj._MyClass__private_method()

In this example, you can see that the access specifiers are more about naming conventions and
developer awareness rather than strict access control. Python trusts developers to use these
conventions correctly. However, it's important to note that Python's philosophy is based on the
idea of "we are all consenting adults here," which means that developers are expected to follow
conventions and respect the intended visibility of attributes and methods.

You might also like