Classes and Objects; Inheritance
• Python supports Functional programming, procedural programming,
Object oriented Programming
• OOP- To relate to real world entities.(objects)
• Every object has
• some property/ attribute/data - store data - variables
• Behaviour - actions – methods(functions)
• Class - a template
• Method or Message - A defined capability of a class
• Field or attribute- A bit of data in a class
• Object or Instance - A particular instance of a class
CONCEPTS
CLASSES - Design – blueprint
OBJECTS - use design to create multiple objects - real entities –
instance of the class
OOP, Defining a Class
• Python was built as a procedural language
• OOP exists and works fine, but feels a bit more "tacked on"
• Java probably does classes better than Python (gasp)
• Declaring a class:
class name:
statements
Fields
name = value
• Example:
class Point:
x = 0
y = 0
# main
p1 = Point()
p1.x = 2
p1.y = -5
• can be declared directly inside class (as shown here)
or in constructors (more common)
• Python does not really have encapsulation or private fields
• relies on caller to "be nice" and not mess with objects' contents
point.py
1
2
3
class Point:
x = 0
y = 0
class addvar:
x = 0
def add(self) :
self.x = self.x + 1
print("So far",self.x)
an = addvar()
an.add()
an.add()
an.add()
This is the template
for making addvar
objects
class is a reserved
word
Each addvar object
has a bit of data
Each addvar object
has a bit of code
Construct a addvar
object and store in an
Tell the an object
to run the add()
code within it
addvar.add(an)
Using a Class
import class
• client programs must import the classes they use
point_main.py
1
2
3
4
5
6
7
8
9
10
from Point import *
# main
p1 = Point()
p1.x = 7
p1.y = -3
...
# Python objects are dynamic (can add fields any time!)
p1.name = "Tyler Durden"
Object Methods
def name(self, parameter, ..., parameter):
statements
• self must be the first parameter to any object method
• represents the "implicit parameter" (this in Java)
• must access the object's fields through the self reference
class Point:
def translate(self, dx, dy):
self.x += dx
self.y += dy
...
"Implicit" Parameter (self)
• Java: this, implicit
public void translate(int dx, int dy) {
x += dx; // this.x += dx;
y += dy; // this.y += dy;
}
• Python: self, explicit
def translate(self, dx, dy):
self.x += dx
self.y += dy
“self” refers to the instance of the class that is currently being
used.
Whenever you call a method of an object created from a class, the
object is automatically passed as the first argument using the
“self” parameter. This enables you to modify the object’s
properties and execute tasks unique to that particular instance.
lf: an argument expected to be the instance from which the method was called
her: an argument expected to be an instance of the class, but not the one calling the method
Create a Point class in Python that represents a point in a 2D space. Implement two
methods:
distance_from_origin(): This method should calculate and return the distance of the
point from the origin (0, 0).
distance_to_other(parameters): This method should calculate and return the distance
between the current point and another point passed as an argument.
Hint: Use the Euclidean distance formula for both methods.
Exercise: Write distance, set_location, and distance_from_origin methods.
point.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from math import *
class Point:
x = 0
y = 0
def set_location(self, x, y):
self.x = x
self.y = y
def distance_from_origin(self):
return sqrt(self.x * self.x + self.y * self.y)
def distance(self, other):
dx = self.x - other.x
dy = self.y - other.y
return sqrt(dx * dx + dy * dy)
Calling Methods
• A client can call the methods of an object in two ways:
• (the value of self can be an implicit or explicit parameter)
1) object.method(parameters)
or
2) Class.method(object, parameters)
• Example:
p = Point(3, -4)
p.translate(1, 5)
Point.translate(p, 1, 5)
Object Lifecycle
• Objects are created, used, and discarded
• We have special blocks of code (methods) that get called
- At the moment of creation (constructor)
- At the moment of destruction (destructor)
• Constructors are used a lot
• Destructors are seldom used
Constructors
def __init__(self, parameter, ..., parameter):
statements
• a constructor is a special method with the name __init__
• Example:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
...
• How would we make it possible to construct a
Point() with no parameters to get (0, 0)?
Destructors
Destructors are called when an object gets destroyed.
In Python, destructors are not needed as much as in C++ because Python has a garbage
collector that handles memory management automatically.
The __del__() method is a known as a destructor method in Python. It is called when all
references to the object have been deleted i.e when an object is garbage collected.
A reference to objects is also deleted when the object goes out of reference or when the
program ends.
Key Concepts:
Reference Counting: Python tracks how many variables reference an object. When the
count drops to zero, the object is ready for garbage collection.
Garbage Collection: This is an automatic process where Python reclaims memory by
destroying objects that are no longer referenced.
Destructors (__del__): These are special methods that are called when an object is about
to be destroyed, allowing for cleanup activities.
class addvar:
x = 0
def __init__(self):
print('I am
constructed')
def add(self) :
self.x = self.x + 1
print('So far',self.x)
def __del__(self):
print('I am destructed',
self.x)
an = addvar()
an.add()
an.add()
an = 42
print('an contains',an)
$ python party4.py
I am constructed
So far 1
So far 2
I am destructed 2
an contains 42
The constructor and destructor are
optional. The constructor is
typically used to set up variables.
The destructor is seldom used.
Many Instances
• We can create lots of objects - the class is the template for the
object
• We can store each distinct object in its own variable
• We call this having multiple instances of the same class
• Each instance has its own copy of the instance variables
Constructors can have
additional parameters.
These can be used to set up
instance variables for the
particular instance of the
class (i.e., for the particular
object).
party5.py
class Party:
x = 0
name = ""
def __init__(self, z):
self.name = z
print(self.name,"constructed")
def party(self) :
self.x = self.x + 1
print(self.name,"party count",self.x)
s = Party("Sally")
j = Party("Jim")
s.party()
j.party()
s.party()
class BirthdayParty:
x = 0
name = ""
def __init__(self, z):
self.name = z
print(self.name,"constructed")
def party(self) :
self.x = self.x + 1
print(self.name,"party count",self.x)
s = BirthdayParty("Sally")
j = BirthdayParty("Jim")
s.party()
j.party()
s.party()
Sally constructed
Jim constructed
Sally party count 1
Jim party count 1
Sally party count 2
toString and __str__
def __str__(self):
return string
• equivalent to Java's toString (converts object to a string)
• invoked automatically when str or print is called
Exercise: Write a __str__ method for Point objects that returns strings like "(3,
-14)"
def __str__(self):
return "(" + str(self.x) + ", " + str(self.y) + ")"
The __str__ method in a Python class is a special
method that returns a string representation of an object
when it's printed or converted to a string. This method
is useful for defining how objects of your class should be
represented as strings, which can make debugging and
logging much easier.
Variables
Size of an object – depends on the no. of variables
Size is allocated by Constructor __init__
Variables
Instance variables – independent copy for
all objects
objectname.instance variables
Class/Static variables – common for all objects
classname.classvariables
hi
Methods
• Constructors
• Instance methods – passing self- for particular
object
• Accessor methods – fetch the variable values getters
functions
• Mutator methods – change the values – setter functions
• Class methods
to work with class variables – use cls and not
self - @classmethod – Decorator before the class
method
then call classname.classmethod()
Methods
• Static method :
• No cls or self
• Common function – not related to class /object
• Classname.staticmethod()
• Decorator -@staticmethod
• Write functions
• PolartoCartesian_Coordinates()x= rcos(theta), y=r
sin(theta)
• CartesiantoPolar_Coordinates()r=sqrt(X^2+y^2)
• Theta= tan-1
(y/x)
• Choose the method(instance/class/static) to be used to
implement
Instance Methods are used when the method needs to interact with or modify the
instance’s data.
Class Methods are useful for creating instances in a controlled way or
accessing/modifying class-level data.
Static Methods are ideal for utility functions that perform operations independent of
instance or class state.
class UserProfile:
def __init__(self, username,
status):
self.username = username
self.status = status
def update_status(self,
new_status):
self.status = new_status
print(f"Status updated
to: {self.status}")
user = UserProfile("johndoe",
"Feeling great!")
user.update_status("Just had a
coffee!")
class Shape:
def __init__(self, name):
self.name = name
@classmethod
def create_circle(cls, radius):
return cls(f"Circle with radius
{radius}")
@classmethod
def create_square(cls, side_length):
return cls(f"Square with side length
{side_length}")
circle = Shape.create_circle(10)
square = Shape.create_square(5)
print(circle.name)
print(square.name)
import math
class MathUtils:
@staticmethod
def gcd(x, y):
return math.gcd(x, y)
print(MathUtils.gcd(48, 18))
You are designing a class for managing an online bookstore. The class will handle different types of operations
related to books and orders. Based on the following descriptions of various functions, determine whether each
function should be an instance method, a class method, or a static method:
add_book(title, author, price)
o This method adds a new book to the bookstore's inventory. It needs to update the inventory which is specific
to each instance of the bookstore.
remove_book(book_id)
o This method removes a book from the bookstore's inventory based on its unique identifier. It needs to modify
the inventory of a specific instance of the bookstore.
get_book_count()
o This method returns the total number of books currently available in the bookstore. It operates based on the
class-level data that tracks the total number of books across all instances.
create_order(order_details)
o This method creates a new order. The details of the order are provided as parameters, and the method does
not depend on any specific instance or class state.
calculate_discount(price, discount_rate)
o This method calculates the discounted price based on the original price and discount rate. It is a utility
function that does not rely on instance or class attributes.
get_store_info()
o This method provides information about the bookstore, such as its name, location, and other store-specific
details. It needs to access class-level attributes.
Instructions:
Identify whether each of the following methods should be implemented as an instance method, a class method, or a
static method
add_book(title, author, price) Instance Method: This method needs to update the inventory,
which is specific to each instance of the bookstore.
remove_book(book_id)Instance Method: This method modifies the inventory of a specific
instance of the bookstore by removing a book. The operation is tied to a particular instance,
making it an instance method.
get_book_count()Class Method: This method returns the total number of books available in
the bookstore. Since it operates based on class-level data that tracks books across all
instances, it should be a class method.
create_order(order_details)Static Method: This method creates a new order based on the
provided details and does not depend on any specific instance or class state.
calculate_discount(price, discount_rate)Static Method: This method calculates the
discounted price using the given parameters.
get_store_info()Class Method: This method provides information about the bookstore, such
as its name and location, which are class-level attributes shared across all instances.
Inheritance
c
Inheritance is one such concept in object oriented programming. Code
reusability being the forte of inheritance, it helps in a lot of applications
when we are working on Python.
The method of inheriting the properties of parent class into a child
class is known as inheritance. It is an OOP concept. Following are the
benefits of inheritance.
1.Code reusability- we do not have to write the same code again and
again, we can just inherit the properties we need in a child class.
2.It represents a real world relationship between parent class and child
class.
3.It is transitive in nature. If a child class inherits properties from a
parent class, then all other sub-classes of the child class will also inherit
the properties of the parent class.
Types of Inheritance
• Depending upon the number of child and parent classes
involved, there are four types of inheritance in python.
Different types of Python Inheritance
There are 5 different types of inheritance in Python. They are as follows:
Single inheritance: When a child class inherits from only one parent class, it is called
single inheritance. We saw an example above.
Multiple inheritances: When a child class inherits from multiple parent classes, it is
called multiple inheritances.
Multilevel inheritance: When we have a child and grandchild relationship. This means that a
child class will inherit from its parent class, which in turn is inheriting from its parent class.
Hierarchical inheritance More than one derived class can be created from a single base.
Hybrid inheritance: This form combines more than one form of inheritance. Basically, it is a
blend of more than one type of inheritance.
Single inheritance
• When a child class inherits only a single parent class.
Class ?
E.g. inheritance in python
class Parent():
def first(self):
print('first function')
class Child(Parent):
def second(self):
print('second function')
ob = Child()
ob.first()
ob.second()
Multilevel Inheritance
• When a child class becomes a parent class for another child
class.
10/19/2024 41
# Python program to demonstrate
# multilevel inheritance
# Base class
class Grandfather:
def __init__(self, grandfathername):
self.grandfathername = grandfathername
# Intermediate class
class Father(Grandfather):
def __init__(self, fathername, grandfathername):
self.fathername = fathername
# invoking constructor of Grandfather class
Grandfather.__init__(self, grandfathername)
# Derived class
class Son(Father):
def __init__(self, sonname, fathername, grandfathername):
self.sonname = sonname
# invoking constructor of Father class
Father.__init__(self, fathername, grandfathername)
def print_name(self):
print('Grandfather name :', self.grandfathername)
print("Father name :", self.fathername)
print("Son name :", self.sonname)
# Driver code
s1 = Son('Prince', 'Rampal', 'Lal mani')
print(s1.grandfathername)
s1.print_name()
Multiple Inheritance
• When a child class inherits from more than one parent
class. Class parent1:
10/19/2024 43
# Python program to demonstrate
# multiple inheritance
# Base class1
class Mother:
mothername = ""
def mother(self):
print(self.mothername)
# Base class2
class Father:
fathername = ""
def father(self):
print(self.fathername)
# Derived class
class Son(Mother, Father):
def parents(self):
print("Father :", self.fathername)
print("Mother :", self.mothername)
# Driver's code
s1 = Son()
s1.fathername = "RAM"
s1.mothername = "SITA"
s1.parents()
Hierarchical Inheritance
• Hierarchical inheritance involves multiple inheritance from
the same base or parent class.
10/19/2024 45
# Python program to demonstrate
# Hierarchical inheritance
# Base class
class Parent:
def func1(self):
print("This function is in parent class.")
# Derived class1
class Child1(Parent):
def func2(self):
print("This function is in child 1.")
# Derivied class2
class Child2(Parent):
def func3(self):
print("This function is in child 2.")
# Driver's code
object1 = Child1()
object2 = Child2()
object1.func1()
object1.func2()
object2.func1()
object2.func3()
Hybrid Inheritance
10/19/2024 47
# Python program to demonstrate
# hybrid inheritance
class School:
def func1(self):
print("This function is in school.")
class Student1(School):
def func2(self):
print("This function is in student 1. ")
class Student2(School):
def func3(self):
print("This function is in student 2.")
class Student3(Student1, School):
def func4(self):
print("This function is in student 3.")
# Driver's code
object = Student3()
object.func1()
object.func2()
Python Inheritance
Inheritance allows us to define a class that inherits all the methods and properties from
another class.
Parent class is the class being inherited from, also called base class.
Child class is the class that inherits from another class, also called derived class.
Add the __init__() Function
The __init__() function is called automatically every time the class is being used to
create a new object.
class Student(Person):
def __init__(self, fname, lname):
#add properties etc.
When you add the __init__() function, the child class will no longer inherit the parent's
__init__() function.
Note: The child's __init__() function overrides the inheritance of the parent's __init__()
function.
To keep the inheritance of the parent's __init__() function, add a call to the parent's
__init__() function:
class Student(Person):
def __init__(self, fname, lname):
Person.__init__(self, fname, lname)
Python also has a super() function that will make the child class inherit all the methods
and properties from its parent:
class Student(Person):
def __init__(self, fname, lname):
super().__init__(fname, lname)
Adding Properties
One of the features that inheritance provides is inheriting the properties of the parent
class as well as adding new properties of our own to the child class
OUTPUT
Method Overriding
Private members of the parent class
We don’t always want the instance variables of the parent class to be inherited by the
child class i.e. we can make some of the instance variables of the parent class private,
which won’t be available to the child class.
In Python inheritance, we can make an instance variable private by adding double
underscores before its name
10/19/2024 55
single leading underscore is used a lot in classes. Programmers can
create private variables and methods, but like the previous examples,
these variables and methods can still be used from the outside. n general,
the single leading underscore is only a naming convention to indicate the
variable or function is for internal use. Programmers are still able to
import the name if they really want.
class Parent:
def __init__(self , fname, fage):
self.firstname = fname
self.age = fage
def view(self):
print(self.firstname , self.age)
class Child(Parent):
def __init__(self , fname , fage):
Parent.__init__(self, fname, fage)
# super().__init__(self, fname, fage)
self.lastname = “Ten"
def view(self):
print(“first name" , self.firstname ,"first came", self.age ,
" years ago." , self.lastname, " is his/her lastname")
ob = Child(“Ben" , '28')
ob.view()
Output: ?
Output ?
class A:
def m(self):
("m of A called")
class B(A):
def m(self):
("m of B called")
class C(A):
def m(self):
("m of C called")
class D(B,C):
def m(self):
("m of D called")
Obj1= D()
Obj1.m()
Output: ?
Output ?
class A:
def m(self):
("m of A called")
class B(A):
def m(self):
("m of B called")
A.m(self)
class C(A):
def m(self):
("m of C called")
A.m(self)
class D(B,C):
def m(self):
("m of D called")
B.m(self) C.m(self)
Obj= D()
Obj.m()
OUTPUT : ?
Output ?
class A:
def m(self):
print("m of A called")
class B(A):
def _m(self):
print("m of B called")
def m(self):
self._m()
A.m(self)
class C(A):
def _m(self):
print("m of C called")
A.m(self)
class D(B,C):
def m(self):
print("m of D called")
B._m(self)
C._m(self)
A.m(self)
d1=D()
d1.m()
10/19/2024 60
• m of D called
• m of B called
• m of C called
• m of A called
• m of A called
Polymorphism
The word polymorphism means having many forms. In
programming, polymorphism means the same function name
(but different signatures) being used for different types.
built poly-morphic functions : len
User defined polymorphic function : with default values
Polymorphism with classes: two classes with same functions
Polymorphism
class Tomato:
def type(self):
print("Vegetable")
def color(self):
print("Red")
class Apple:
def type(self):
print("Fruit")
def color(self):
print("Red")
Output :
def func(obj):
obj.type()
obj.color()
obj_tomato = Tomato()
obj_apple = Apple()
func(obj_tomato)
func(obj_apple)
Underscore(_) in python
Single standalone underscore _
Single standalone underscore _ is a valid character for a
Python identifier, so it can be used as a variable name.
• Represent the last expression in the interpreter
• According to Python doc, the special identifier _ is used in the interactive
interpreter to store the result of the last evaluation. It is stored in the
builtin module.
• >>> '_' in dir(__builtins__)
False
>>> 1+1
2
>>> '_' in dir(__builtins__)
True
>>> _
2
Underscore (_) in python
Single standalone underscore _ can also be
used as a visual separator for digit grouping
purposes.
it works for integers, floating-point, and
complex number literals.
i = 1_000
amount = 1_000_000.1
binary = 0b_0100_1110
hex = 0xCAFE_F00D
>>> print(amount)
1000000.1
>>> print(binary)
78
Underscore(_V) in python
Single leading underscore:
_var is intended for internal use.
from Module import *
doesn’t import objects whose names
start with an underscore
Underscore(_V) in python
It warns the developer that
this variable, method, or
function is not supposed to
be imported and used
publicly. Nevertheless,
Python doesn’t completely
prevent them from being
imported and used.
Eg,
#m.py
external = "external"
_internal = "internal"
# main1.py - doesn't work
from m import *
print(external)
# external
print(_internal)
# NameError: name '_internal' is not
defined
# main2.py - works
from m import external, _internal
print(external)
# external
print(_internal)
# internal
# main3.py - works
import m
print(m.external)
# external
print(m._internal)
# internal
Underscore(_V) in python
single leading underscore is used a
lot in classes. Programmers can
create private variables and methods,
but like the previous examples, these
variables and methods can still be
used from the outside. n general, the
single leading underscore is only a
naming convention to indicate the
variable or function is for internal use.
Programmers are still able to import
the name if they really want.
class Point:
def __init__(self,x1,y1):
self.x=x1
self._y=y1
def disp(self):
print(self.x, self._y)
p1 = Point(4,5)
p1._y=25
p1.disp()
Underscore(_ _V) in python
Double Leading Underscore _ _var
Python interpreter will do name
mangling to identifiers with leading
underscores. Name mangling is the
process to overwrite such identifiers
in a class to avoid conflicts of names
between the current class and its
subclasses.
Will see in inheritance
class Point:
def __init__(self,x1,y1):
self.x=x1
self._ _y=y1
def disp(self):
print(self.x, self._ _y)
p1 = Point(4,5)
p1._ _y=25
print(p1._ _y)
p1.disp()
print(dir(p1))
Name mangling is a technique we use to
protect instance variables from being
accidentally overwritten or shadowed by
instance variables with the same name in
derived classes.
Name mangling works by adding a double
underscore prefix to the name of an instance
variable, and replacing any occurrences of the
underscore character in the name with an
10/19/2024 69
10/19/2024 70
10/19/2024 71
Underscore(V_) in python
Single Trailing Underscore
VAR_
There is only one reason to use single
trailing underscore which is to avoid
conflicts with Python keywords.
import keyword
print(keyword.kwlist)
False_
Underscore(V_ _) in python
Double trailing underscores Unlike
single trailing underscore, there is no
special meaning for double trailing
underscores. You can probably think
of var_ _ as an extension of var_.
import keyword
print(keyword.kwlist)
True_ _
Underscore(_ _V_ _) in python
Double Leading and Trailing
Underscore _ _var_ _
Python doesn’t apply name mangling
to such attributes, but names with
double leading and trailing
underscores are reserved for special
use in Python. They are called Magic
Names.
import keyword
print(keyword.kwlist)
True_ _
Underscore(_ _V_ _) in python
Double Leading and Trailing
Underscore _ _var_ _
These magic attributes and magic
methods are allowed to be
overwritten, but you need to know
what you are doing.
E.g. _ _str_ _ is a method that returns
the string representation of the
object. This method is called when
you do print() or str(). In the example,
we overwrite the string presentation,
then we will get a new format in the
output.
def __str__(self):
return str("("+str(self.x)
+","+str(self.__y)+")")
You are allowed to have a customized
name with double leading and trailing
underscore like __disp__. Python will
just take it as a regular attribute name
and will not apply name mangling on
it. However, it’s not a good practice to
have a name like this without a strong
reason.
10/19/2024 76
• In Python, the underscore (_) has several meanings and uses, which can vary based on the
context. It's a versatile character that can be used in interpreter, for ignoring values, in
variable naming, and more.
• Single and Double Underscores
• Single Underscore:
• In the Interpreter: The underscore is used to hold the result of the last executed expression
in an interactive session, allowing you to reuse this value in subsequent operations.
• For Ignoring Values: When unpacking values, if you're not interested in one or more values,
you can assign them to the underscore, indicating that these are throwaway variables.
• After a Name: If a variable name conflicts with a Python keyword, appending an underscore
to the name is a common practice to avoid such conflicts.
• Before a Name: A single underscore before a name is used to indicate that the attribute or
method is intended for internal use. This convention suggests that it should not be accessed
from outside the class or module.
• In Numeric Literals: Underscores can be used in numeric literals to improve readability by
separating digits, similar to how commas or spaces are used as thousand separators.
• Double Underscores:
• Before a Name (Name Mangling): Double underscores at the beginning of a name are
used by the Python interpreter to rename attributes in a class to avoid naming conflicts with
subclasses. This process is known as name mangling.
• Before and After a Name (Dunder Methods): Names that begin and end with double
underscores are special methods in Python. These are often referred to as "dunder"
methods (short for "double underscore") and include built-in methods like __init__ for
object constructors or __str__ for string representations.
Dunder or Magic methods
Dunder or magic methods in Python are the methods
having two prefix and suffix underscores in the method
name. Dunder here means “Double Under (Underscores)”.
These are commonly used for operator overloading.
Few examples for magic methods are: __init__, __add__,
__len__, __repr__ etc.
The __init__ method for initialization is invoked without any
call, when an instance of a class is created, like constructors
in certain other programming languages such as C++, Java,
C#, PHP etc. These methods are the reason we can add two
strings with ‘+’ operator without any explicit typecasting.
Operator overloading
class Point:
def __init__(self,x1,y1):
self.x=x1
self.__y=y1
def disp(self):
print(self.x, self.__y)
def __add__(self, other):
return(Point(self.x+other.x,self.__y+other.__y))
def __str__(self):
return str("("+str(self.x)+","+str(self.__y)+")")
p1 = Point(4,5)
p2 = Point(10,20)
p3=p1+p2
print(p3)
Getters and setters
In Python, getters and setters are not the same as those
in other object-oriented programming languages.
Basically, the main purpose of using getters and setters
in object-oriented programs is to ensure data
encapsulation. Private variables in python are not
actually hidden fields like in other object oriented
languages. Getters and Setters in python are often used
when:
• We use getters & setters to add validation logic around
getting and setting a value.
• To avoid direct access of a class field i.e. private
variables cannot be accessed directly or modified by
external user.
• Getters are the methods that are used in Object-Oriented
Programming (OOP) to access a class's private attributes.
The setattr() function in Python corresponds to
the getattr() function in Python. It alters an object's
attribute values.
• The setter is a method that is used to set the property's
value. It is very useful in object-oriented programming to
set the value of private attributes in a class.
Normal functions as getters and setters
class Point:
num_points=0
def __init__(self,a,b):
self.x=a
self.y=b
Point.num_points=Point.num_points+1
def disp(self):
print(self.x,"", self.y, end=" ")
def getxy(self):
return (self.x,self.y)
def setxy(self,a,b):
self.x=a
self.y=b
p1 = Point(4,5)
p2 = Point(10,20)
print(p1.getxy())
p2.setxy(100,200)
print(p2.getxy())
p1.x=10
p1.y=20
Using property() function to achieve getters and
setters behaviour
def setx(self,a):
self._x=a
print("setter")
def sety(self, b):
self._y=b
def __str__(self):
return str("("+str(self.x)
+","+str(self.y)+")")
x = property(getx, setx)
y = property(gety, sety)
p1 = Point(4,5)
p2 = Point(10,20)
print(p1.x)
p2.x=10
print(p2.x)
ass Point:
num_points=0
def __init__(self,a,b):
self._x=a
self._y=b
Point.num_points=Point.num_points+1
def disp(self):
print(self._x,"", self._y, end=" ")
def getx(self):
print("getter")
return (self._x)
def gety(self):
return self._y
10/19/2024 85
class Circle:
def __init__(self, radius):
self._radius = radius # The underscore indicates this is a "protected" attribute
# Getter for radius
@property
def radius(self):
return self._radius
# Setter for radius
@radius.setter
def radius(self, value):
if value < 0:
raise ValueError("Radius cannot be negative")
self._radius = value
# Method to calculate area
def area(self):
return 3.14159 * self._radius ** 2
# Usage
c = Circle(5)
print(c.radius) # Uses the getter, output: 5
print(c.area()) # Output: 78.53975
c.radius = 10 # Uses the setter
print(c.radius) # Output: 10
print(c.area()) # Output: 314.159
# c.radius = -5 # Uncommenting this will raise a ValueError
Using the @property decorators to achieve getters and
setter behavior:
@x.setter
def x(self,a):
self._x=a
print("setter")
@y.setter
def y(self, b):
self._y=b
p1 = Point(4,5)
p2 = Point(10,20)
print(p1.x)
p2.x=10
print(p2.x)
@property is one of Python's built-in decorators. The primary goal of
any decorator is to modify the class methods or attributes so that the
class user does not need to change their code.
class Point:
num_points=0
def __init__(self,a,b):
self._x=a
self._y=b
Point.num_points=Point.num_points+1
def disp(self):
print(self._x,"", self._y, end=" ")
@property
def x(self):
print("getter")
return (self._x)
@property
def y(self):
return self._y

Python 2. classes- cruciql for students objects1.pptx

  • 1.
  • 2.
    • Python supportsFunctional programming, procedural programming, Object oriented Programming
  • 3.
    • OOP- Torelate to real world entities.(objects) • Every object has • some property/ attribute/data - store data - variables • Behaviour - actions – methods(functions) • Class - a template • Method or Message - A defined capability of a class • Field or attribute- A bit of data in a class • Object or Instance - A particular instance of a class
  • 4.
    CONCEPTS CLASSES - Design– blueprint OBJECTS - use design to create multiple objects - real entities – instance of the class
  • 5.
    OOP, Defining aClass • Python was built as a procedural language • OOP exists and works fine, but feels a bit more "tacked on" • Java probably does classes better than Python (gasp) • Declaring a class: class name: statements
  • 6.
    Fields name = value •Example: class Point: x = 0 y = 0 # main p1 = Point() p1.x = 2 p1.y = -5 • can be declared directly inside class (as shown here) or in constructors (more common) • Python does not really have encapsulation or private fields • relies on caller to "be nice" and not mess with objects' contents point.py 1 2 3 class Point: x = 0 y = 0
  • 7.
    class addvar: x =0 def add(self) : self.x = self.x + 1 print("So far",self.x) an = addvar() an.add() an.add() an.add() This is the template for making addvar objects class is a reserved word Each addvar object has a bit of data Each addvar object has a bit of code Construct a addvar object and store in an Tell the an object to run the add() code within it addvar.add(an)
  • 8.
    Using a Class importclass • client programs must import the classes they use point_main.py 1 2 3 4 5 6 7 8 9 10 from Point import * # main p1 = Point() p1.x = 7 p1.y = -3 ... # Python objects are dynamic (can add fields any time!) p1.name = "Tyler Durden"
  • 9.
    Object Methods def name(self,parameter, ..., parameter): statements • self must be the first parameter to any object method • represents the "implicit parameter" (this in Java) • must access the object's fields through the self reference class Point: def translate(self, dx, dy): self.x += dx self.y += dy ...
  • 10.
    "Implicit" Parameter (self) •Java: this, implicit public void translate(int dx, int dy) { x += dx; // this.x += dx; y += dy; // this.y += dy; } • Python: self, explicit def translate(self, dx, dy): self.x += dx self.y += dy “self” refers to the instance of the class that is currently being used. Whenever you call a method of an object created from a class, the object is automatically passed as the first argument using the “self” parameter. This enables you to modify the object’s properties and execute tasks unique to that particular instance.
  • 11.
    lf: an argumentexpected to be the instance from which the method was called her: an argument expected to be an instance of the class, but not the one calling the method
  • 12.
    Create a Pointclass in Python that represents a point in a 2D space. Implement two methods: distance_from_origin(): This method should calculate and return the distance of the point from the origin (0, 0). distance_to_other(parameters): This method should calculate and return the distance between the current point and another point passed as an argument. Hint: Use the Euclidean distance formula for both methods.
  • 13.
    Exercise: Write distance,set_location, and distance_from_origin methods. point.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from math import * class Point: x = 0 y = 0 def set_location(self, x, y): self.x = x self.y = y def distance_from_origin(self): return sqrt(self.x * self.x + self.y * self.y) def distance(self, other): dx = self.x - other.x dy = self.y - other.y return sqrt(dx * dx + dy * dy)
  • 14.
    Calling Methods • Aclient can call the methods of an object in two ways: • (the value of self can be an implicit or explicit parameter) 1) object.method(parameters) or 2) Class.method(object, parameters) • Example: p = Point(3, -4) p.translate(1, 5) Point.translate(p, 1, 5)
  • 15.
    Object Lifecycle • Objectsare created, used, and discarded • We have special blocks of code (methods) that get called - At the moment of creation (constructor) - At the moment of destruction (destructor) • Constructors are used a lot • Destructors are seldom used
  • 16.
    Constructors def __init__(self, parameter,..., parameter): statements • a constructor is a special method with the name __init__ • Example: class Point: def __init__(self, x, y): self.x = x self.y = y ... • How would we make it possible to construct a Point() with no parameters to get (0, 0)?
  • 17.
    Destructors Destructors are calledwhen an object gets destroyed. In Python, destructors are not needed as much as in C++ because Python has a garbage collector that handles memory management automatically. The __del__() method is a known as a destructor method in Python. It is called when all references to the object have been deleted i.e when an object is garbage collected. A reference to objects is also deleted when the object goes out of reference or when the program ends. Key Concepts: Reference Counting: Python tracks how many variables reference an object. When the count drops to zero, the object is ready for garbage collection. Garbage Collection: This is an automatic process where Python reclaims memory by destroying objects that are no longer referenced. Destructors (__del__): These are special methods that are called when an object is about to be destroyed, allowing for cleanup activities.
  • 18.
    class addvar: x =0 def __init__(self): print('I am constructed') def add(self) : self.x = self.x + 1 print('So far',self.x) def __del__(self): print('I am destructed', self.x) an = addvar() an.add() an.add() an = 42 print('an contains',an) $ python party4.py I am constructed So far 1 So far 2 I am destructed 2 an contains 42 The constructor and destructor are optional. The constructor is typically used to set up variables. The destructor is seldom used.
  • 20.
    Many Instances • Wecan create lots of objects - the class is the template for the object • We can store each distinct object in its own variable • We call this having multiple instances of the same class • Each instance has its own copy of the instance variables
  • 21.
    Constructors can have additionalparameters. These can be used to set up instance variables for the particular instance of the class (i.e., for the particular object). party5.py class Party: x = 0 name = "" def __init__(self, z): self.name = z print(self.name,"constructed") def party(self) : self.x = self.x + 1 print(self.name,"party count",self.x) s = Party("Sally") j = Party("Jim") s.party() j.party() s.party()
  • 22.
    class BirthdayParty: x =0 name = "" def __init__(self, z): self.name = z print(self.name,"constructed") def party(self) : self.x = self.x + 1 print(self.name,"party count",self.x) s = BirthdayParty("Sally") j = BirthdayParty("Jim") s.party() j.party() s.party() Sally constructed Jim constructed Sally party count 1 Jim party count 1 Sally party count 2
  • 23.
    toString and __str__ def__str__(self): return string • equivalent to Java's toString (converts object to a string) • invoked automatically when str or print is called Exercise: Write a __str__ method for Point objects that returns strings like "(3, -14)" def __str__(self): return "(" + str(self.x) + ", " + str(self.y) + ")" The __str__ method in a Python class is a special method that returns a string representation of an object when it's printed or converted to a string. This method is useful for defining how objects of your class should be represented as strings, which can make debugging and logging much easier.
  • 25.
    Variables Size of anobject – depends on the no. of variables Size is allocated by Constructor __init__ Variables Instance variables – independent copy for all objects objectname.instance variables Class/Static variables – common for all objects classname.classvariables hi
  • 27.
    Methods • Constructors • Instancemethods – passing self- for particular object • Accessor methods – fetch the variable values getters functions • Mutator methods – change the values – setter functions • Class methods to work with class variables – use cls and not self - @classmethod – Decorator before the class method then call classname.classmethod()
  • 28.
    Methods • Static method: • No cls or self • Common function – not related to class /object • Classname.staticmethod() • Decorator -@staticmethod • Write functions • PolartoCartesian_Coordinates()x= rcos(theta), y=r sin(theta) • CartesiantoPolar_Coordinates()r=sqrt(X^2+y^2) • Theta= tan-1 (y/x) • Choose the method(instance/class/static) to be used to implement
  • 29.
    Instance Methods areused when the method needs to interact with or modify the instance’s data. Class Methods are useful for creating instances in a controlled way or accessing/modifying class-level data. Static Methods are ideal for utility functions that perform operations independent of instance or class state.
  • 30.
    class UserProfile: def __init__(self,username, status): self.username = username self.status = status def update_status(self, new_status): self.status = new_status print(f"Status updated to: {self.status}") user = UserProfile("johndoe", "Feeling great!") user.update_status("Just had a coffee!")
  • 31.
    class Shape: def __init__(self,name): self.name = name @classmethod def create_circle(cls, radius): return cls(f"Circle with radius {radius}") @classmethod def create_square(cls, side_length): return cls(f"Square with side length {side_length}") circle = Shape.create_circle(10) square = Shape.create_square(5) print(circle.name) print(square.name)
  • 32.
    import math class MathUtils: @staticmethod defgcd(x, y): return math.gcd(x, y) print(MathUtils.gcd(48, 18))
  • 33.
    You are designinga class for managing an online bookstore. The class will handle different types of operations related to books and orders. Based on the following descriptions of various functions, determine whether each function should be an instance method, a class method, or a static method: add_book(title, author, price) o This method adds a new book to the bookstore's inventory. It needs to update the inventory which is specific to each instance of the bookstore. remove_book(book_id) o This method removes a book from the bookstore's inventory based on its unique identifier. It needs to modify the inventory of a specific instance of the bookstore. get_book_count() o This method returns the total number of books currently available in the bookstore. It operates based on the class-level data that tracks the total number of books across all instances. create_order(order_details) o This method creates a new order. The details of the order are provided as parameters, and the method does not depend on any specific instance or class state. calculate_discount(price, discount_rate) o This method calculates the discounted price based on the original price and discount rate. It is a utility function that does not rely on instance or class attributes. get_store_info() o This method provides information about the bookstore, such as its name, location, and other store-specific details. It needs to access class-level attributes. Instructions: Identify whether each of the following methods should be implemented as an instance method, a class method, or a static method
  • 34.
    add_book(title, author, price)Instance Method: This method needs to update the inventory, which is specific to each instance of the bookstore. remove_book(book_id)Instance Method: This method modifies the inventory of a specific instance of the bookstore by removing a book. The operation is tied to a particular instance, making it an instance method. get_book_count()Class Method: This method returns the total number of books available in the bookstore. Since it operates based on class-level data that tracks books across all instances, it should be a class method. create_order(order_details)Static Method: This method creates a new order based on the provided details and does not depend on any specific instance or class state. calculate_discount(price, discount_rate)Static Method: This method calculates the discounted price using the given parameters. get_store_info()Class Method: This method provides information about the bookstore, such as its name and location, which are class-level attributes shared across all instances.
  • 35.
    Inheritance c Inheritance is onesuch concept in object oriented programming. Code reusability being the forte of inheritance, it helps in a lot of applications when we are working on Python. The method of inheriting the properties of parent class into a child class is known as inheritance. It is an OOP concept. Following are the benefits of inheritance. 1.Code reusability- we do not have to write the same code again and again, we can just inherit the properties we need in a child class. 2.It represents a real world relationship between parent class and child class. 3.It is transitive in nature. If a child class inherits properties from a parent class, then all other sub-classes of the child class will also inherit the properties of the parent class.
  • 36.
    Types of Inheritance •Depending upon the number of child and parent classes involved, there are four types of inheritance in python.
  • 37.
    Different types ofPython Inheritance There are 5 different types of inheritance in Python. They are as follows: Single inheritance: When a child class inherits from only one parent class, it is called single inheritance. We saw an example above. Multiple inheritances: When a child class inherits from multiple parent classes, it is called multiple inheritances. Multilevel inheritance: When we have a child and grandchild relationship. This means that a child class will inherit from its parent class, which in turn is inheriting from its parent class. Hierarchical inheritance More than one derived class can be created from a single base. Hybrid inheritance: This form combines more than one form of inheritance. Basically, it is a blend of more than one type of inheritance.
  • 38.
    Single inheritance • Whena child class inherits only a single parent class. Class ?
  • 39.
    E.g. inheritance inpython class Parent(): def first(self): print('first function') class Child(Parent): def second(self): print('second function') ob = Child() ob.first() ob.second()
  • 40.
    Multilevel Inheritance • Whena child class becomes a parent class for another child class.
  • 41.
    10/19/2024 41 # Pythonprogram to demonstrate # multilevel inheritance # Base class class Grandfather: def __init__(self, grandfathername): self.grandfathername = grandfathername # Intermediate class class Father(Grandfather): def __init__(self, fathername, grandfathername): self.fathername = fathername # invoking constructor of Grandfather class Grandfather.__init__(self, grandfathername) # Derived class class Son(Father): def __init__(self, sonname, fathername, grandfathername): self.sonname = sonname # invoking constructor of Father class Father.__init__(self, fathername, grandfathername) def print_name(self): print('Grandfather name :', self.grandfathername) print("Father name :", self.fathername) print("Son name :", self.sonname) # Driver code s1 = Son('Prince', 'Rampal', 'Lal mani') print(s1.grandfathername) s1.print_name()
  • 42.
    Multiple Inheritance • Whena child class inherits from more than one parent class. Class parent1:
  • 43.
    10/19/2024 43 # Pythonprogram to demonstrate # multiple inheritance # Base class1 class Mother: mothername = "" def mother(self): print(self.mothername) # Base class2 class Father: fathername = "" def father(self): print(self.fathername) # Derived class class Son(Mother, Father): def parents(self): print("Father :", self.fathername) print("Mother :", self.mothername) # Driver's code s1 = Son() s1.fathername = "RAM" s1.mothername = "SITA" s1.parents()
  • 44.
    Hierarchical Inheritance • Hierarchicalinheritance involves multiple inheritance from the same base or parent class.
  • 45.
    10/19/2024 45 # Pythonprogram to demonstrate # Hierarchical inheritance # Base class class Parent: def func1(self): print("This function is in parent class.") # Derived class1 class Child1(Parent): def func2(self): print("This function is in child 1.") # Derivied class2 class Child2(Parent): def func3(self): print("This function is in child 2.") # Driver's code object1 = Child1() object2 = Child2() object1.func1() object1.func2() object2.func1() object2.func3()
  • 46.
  • 47.
    10/19/2024 47 # Pythonprogram to demonstrate # hybrid inheritance class School: def func1(self): print("This function is in school.") class Student1(School): def func2(self): print("This function is in student 1. ") class Student2(School): def func3(self): print("This function is in student 2.") class Student3(Student1, School): def func4(self): print("This function is in student 3.") # Driver's code object = Student3() object.func1() object.func2()
  • 48.
    Python Inheritance Inheritance allowsus to define a class that inherits all the methods and properties from another class. Parent class is the class being inherited from, also called base class. Child class is the class that inherits from another class, also called derived class.
  • 50.
    Add the __init__()Function The __init__() function is called automatically every time the class is being used to create a new object. class Student(Person): def __init__(self, fname, lname): #add properties etc. When you add the __init__() function, the child class will no longer inherit the parent's __init__() function. Note: The child's __init__() function overrides the inheritance of the parent's __init__() function. To keep the inheritance of the parent's __init__() function, add a call to the parent's __init__() function: class Student(Person): def __init__(self, fname, lname): Person.__init__(self, fname, lname)
  • 51.
    Python also hasa super() function that will make the child class inherit all the methods and properties from its parent: class Student(Person): def __init__(self, fname, lname): super().__init__(fname, lname)
  • 52.
    Adding Properties One ofthe features that inheritance provides is inheriting the properties of the parent class as well as adding new properties of our own to the child class OUTPUT
  • 53.
  • 54.
    Private members ofthe parent class We don’t always want the instance variables of the parent class to be inherited by the child class i.e. we can make some of the instance variables of the parent class private, which won’t be available to the child class. In Python inheritance, we can make an instance variable private by adding double underscores before its name
  • 55.
    10/19/2024 55 single leadingunderscore is used a lot in classes. Programmers can create private variables and methods, but like the previous examples, these variables and methods can still be used from the outside. n general, the single leading underscore is only a naming convention to indicate the variable or function is for internal use. Programmers are still able to import the name if they really want.
  • 56.
    class Parent: def __init__(self, fname, fage): self.firstname = fname self.age = fage def view(self): print(self.firstname , self.age) class Child(Parent): def __init__(self , fname , fage): Parent.__init__(self, fname, fage) # super().__init__(self, fname, fage) self.lastname = “Ten" def view(self): print(“first name" , self.firstname ,"first came", self.age , " years ago." , self.lastname, " is his/her lastname") ob = Child(“Ben" , '28') ob.view() Output: ?
  • 57.
    Output ? class A: defm(self): ("m of A called") class B(A): def m(self): ("m of B called") class C(A): def m(self): ("m of C called") class D(B,C): def m(self): ("m of D called") Obj1= D() Obj1.m() Output: ?
  • 58.
    Output ? class A: defm(self): ("m of A called") class B(A): def m(self): ("m of B called") A.m(self) class C(A): def m(self): ("m of C called") A.m(self) class D(B,C): def m(self): ("m of D called") B.m(self) C.m(self) Obj= D() Obj.m() OUTPUT : ?
  • 59.
    Output ? class A: defm(self): print("m of A called") class B(A): def _m(self): print("m of B called") def m(self): self._m() A.m(self) class C(A): def _m(self): print("m of C called") A.m(self) class D(B,C): def m(self): print("m of D called") B._m(self) C._m(self) A.m(self) d1=D() d1.m()
  • 60.
    10/19/2024 60 • mof D called • m of B called • m of C called • m of A called • m of A called
  • 61.
    Polymorphism The word polymorphismmeans having many forms. In programming, polymorphism means the same function name (but different signatures) being used for different types. built poly-morphic functions : len User defined polymorphic function : with default values Polymorphism with classes: two classes with same functions
  • 62.
    Polymorphism class Tomato: def type(self): print("Vegetable") defcolor(self): print("Red") class Apple: def type(self): print("Fruit") def color(self): print("Red") Output : def func(obj): obj.type() obj.color() obj_tomato = Tomato() obj_apple = Apple() func(obj_tomato) func(obj_apple)
  • 63.
    Underscore(_) in python Singlestandalone underscore _ Single standalone underscore _ is a valid character for a Python identifier, so it can be used as a variable name. • Represent the last expression in the interpreter • According to Python doc, the special identifier _ is used in the interactive interpreter to store the result of the last evaluation. It is stored in the builtin module. • >>> '_' in dir(__builtins__) False >>> 1+1 2 >>> '_' in dir(__builtins__) True >>> _ 2
  • 64.
    Underscore (_) inpython Single standalone underscore _ can also be used as a visual separator for digit grouping purposes. it works for integers, floating-point, and complex number literals. i = 1_000 amount = 1_000_000.1 binary = 0b_0100_1110 hex = 0xCAFE_F00D >>> print(amount) 1000000.1 >>> print(binary) 78
  • 65.
    Underscore(_V) in python Singleleading underscore: _var is intended for internal use. from Module import * doesn’t import objects whose names start with an underscore
  • 66.
    Underscore(_V) in python Itwarns the developer that this variable, method, or function is not supposed to be imported and used publicly. Nevertheless, Python doesn’t completely prevent them from being imported and used. Eg, #m.py external = "external" _internal = "internal" # main1.py - doesn't work from m import * print(external) # external print(_internal) # NameError: name '_internal' is not defined # main2.py - works from m import external, _internal print(external) # external print(_internal) # internal # main3.py - works import m print(m.external) # external print(m._internal) # internal
  • 67.
    Underscore(_V) in python singleleading underscore is used a lot in classes. Programmers can create private variables and methods, but like the previous examples, these variables and methods can still be used from the outside. n general, the single leading underscore is only a naming convention to indicate the variable or function is for internal use. Programmers are still able to import the name if they really want. class Point: def __init__(self,x1,y1): self.x=x1 self._y=y1 def disp(self): print(self.x, self._y) p1 = Point(4,5) p1._y=25 p1.disp()
  • 68.
    Underscore(_ _V) inpython Double Leading Underscore _ _var Python interpreter will do name mangling to identifiers with leading underscores. Name mangling is the process to overwrite such identifiers in a class to avoid conflicts of names between the current class and its subclasses. Will see in inheritance class Point: def __init__(self,x1,y1): self.x=x1 self._ _y=y1 def disp(self): print(self.x, self._ _y) p1 = Point(4,5) p1._ _y=25 print(p1._ _y) p1.disp() print(dir(p1)) Name mangling is a technique we use to protect instance variables from being accidentally overwritten or shadowed by instance variables with the same name in derived classes. Name mangling works by adding a double underscore prefix to the name of an instance variable, and replacing any occurrences of the underscore character in the name with an
  • 69.
  • 70.
  • 71.
  • 72.
    Underscore(V_) in python SingleTrailing Underscore VAR_ There is only one reason to use single trailing underscore which is to avoid conflicts with Python keywords. import keyword print(keyword.kwlist) False_
  • 73.
    Underscore(V_ _) inpython Double trailing underscores Unlike single trailing underscore, there is no special meaning for double trailing underscores. You can probably think of var_ _ as an extension of var_. import keyword print(keyword.kwlist) True_ _
  • 74.
    Underscore(_ _V_ _)in python Double Leading and Trailing Underscore _ _var_ _ Python doesn’t apply name mangling to such attributes, but names with double leading and trailing underscores are reserved for special use in Python. They are called Magic Names. import keyword print(keyword.kwlist) True_ _
  • 75.
    Underscore(_ _V_ _)in python Double Leading and Trailing Underscore _ _var_ _ These magic attributes and magic methods are allowed to be overwritten, but you need to know what you are doing. E.g. _ _str_ _ is a method that returns the string representation of the object. This method is called when you do print() or str(). In the example, we overwrite the string presentation, then we will get a new format in the output. def __str__(self): return str("("+str(self.x) +","+str(self.__y)+")") You are allowed to have a customized name with double leading and trailing underscore like __disp__. Python will just take it as a regular attribute name and will not apply name mangling on it. However, it’s not a good practice to have a name like this without a strong reason.
  • 76.
    10/19/2024 76 • InPython, the underscore (_) has several meanings and uses, which can vary based on the context. It's a versatile character that can be used in interpreter, for ignoring values, in variable naming, and more. • Single and Double Underscores • Single Underscore: • In the Interpreter: The underscore is used to hold the result of the last executed expression in an interactive session, allowing you to reuse this value in subsequent operations. • For Ignoring Values: When unpacking values, if you're not interested in one or more values, you can assign them to the underscore, indicating that these are throwaway variables. • After a Name: If a variable name conflicts with a Python keyword, appending an underscore to the name is a common practice to avoid such conflicts. • Before a Name: A single underscore before a name is used to indicate that the attribute or method is intended for internal use. This convention suggests that it should not be accessed from outside the class or module. • In Numeric Literals: Underscores can be used in numeric literals to improve readability by separating digits, similar to how commas or spaces are used as thousand separators. • Double Underscores: • Before a Name (Name Mangling): Double underscores at the beginning of a name are used by the Python interpreter to rename attributes in a class to avoid naming conflicts with subclasses. This process is known as name mangling. • Before and After a Name (Dunder Methods): Names that begin and end with double underscores are special methods in Python. These are often referred to as "dunder" methods (short for "double underscore") and include built-in methods like __init__ for object constructors or __str__ for string representations.
  • 77.
    Dunder or Magicmethods Dunder or magic methods in Python are the methods having two prefix and suffix underscores in the method name. Dunder here means “Double Under (Underscores)”. These are commonly used for operator overloading. Few examples for magic methods are: __init__, __add__, __len__, __repr__ etc. The __init__ method for initialization is invoked without any call, when an instance of a class is created, like constructors in certain other programming languages such as C++, Java, C#, PHP etc. These methods are the reason we can add two strings with ‘+’ operator without any explicit typecasting.
  • 78.
    Operator overloading class Point: def__init__(self,x1,y1): self.x=x1 self.__y=y1 def disp(self): print(self.x, self.__y) def __add__(self, other): return(Point(self.x+other.x,self.__y+other.__y)) def __str__(self): return str("("+str(self.x)+","+str(self.__y)+")") p1 = Point(4,5) p2 = Point(10,20) p3=p1+p2 print(p3)
  • 81.
    Getters and setters InPython, getters and setters are not the same as those in other object-oriented programming languages. Basically, the main purpose of using getters and setters in object-oriented programs is to ensure data encapsulation. Private variables in python are not actually hidden fields like in other object oriented languages. Getters and Setters in python are often used when: • We use getters & setters to add validation logic around getting and setting a value. • To avoid direct access of a class field i.e. private variables cannot be accessed directly or modified by external user.
  • 82.
    • Getters arethe methods that are used in Object-Oriented Programming (OOP) to access a class's private attributes. The setattr() function in Python corresponds to the getattr() function in Python. It alters an object's attribute values. • The setter is a method that is used to set the property's value. It is very useful in object-oriented programming to set the value of private attributes in a class.
  • 83.
    Normal functions asgetters and setters class Point: num_points=0 def __init__(self,a,b): self.x=a self.y=b Point.num_points=Point.num_points+1 def disp(self): print(self.x,"", self.y, end=" ") def getxy(self): return (self.x,self.y) def setxy(self,a,b): self.x=a self.y=b p1 = Point(4,5) p2 = Point(10,20) print(p1.getxy()) p2.setxy(100,200) print(p2.getxy()) p1.x=10 p1.y=20
  • 84.
    Using property() functionto achieve getters and setters behaviour def setx(self,a): self._x=a print("setter") def sety(self, b): self._y=b def __str__(self): return str("("+str(self.x) +","+str(self.y)+")") x = property(getx, setx) y = property(gety, sety) p1 = Point(4,5) p2 = Point(10,20) print(p1.x) p2.x=10 print(p2.x) ass Point: num_points=0 def __init__(self,a,b): self._x=a self._y=b Point.num_points=Point.num_points+1 def disp(self): print(self._x,"", self._y, end=" ") def getx(self): print("getter") return (self._x) def gety(self): return self._y
  • 85.
    10/19/2024 85 class Circle: def__init__(self, radius): self._radius = radius # The underscore indicates this is a "protected" attribute # Getter for radius @property def radius(self): return self._radius # Setter for radius @radius.setter def radius(self, value): if value < 0: raise ValueError("Radius cannot be negative") self._radius = value # Method to calculate area def area(self): return 3.14159 * self._radius ** 2 # Usage c = Circle(5) print(c.radius) # Uses the getter, output: 5 print(c.area()) # Output: 78.53975 c.radius = 10 # Uses the setter print(c.radius) # Output: 10 print(c.area()) # Output: 314.159 # c.radius = -5 # Uncommenting this will raise a ValueError
  • 86.
    Using the @propertydecorators to achieve getters and setter behavior: @x.setter def x(self,a): self._x=a print("setter") @y.setter def y(self, b): self._y=b p1 = Point(4,5) p2 = Point(10,20) print(p1.x) p2.x=10 print(p2.x) @property is one of Python's built-in decorators. The primary goal of any decorator is to modify the class methods or attributes so that the class user does not need to change their code. class Point: num_points=0 def __init__(self,a,b): self._x=a self._y=b Point.num_points=Point.num_points+1 def disp(self): print(self._x,"", self._y, end=" ") @property def x(self): print("getter") return (self._x) @property def y(self): return self._y