-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpillars_of_oops.py
More file actions
163 lines (125 loc) · 4.4 KB
/
Copy pathpillars_of_oops.py
File metadata and controls
163 lines (125 loc) · 4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# 4 pillars of OOPS
# 1. ABSTRACTION - hiding implementation details(unnecessary features) of a class and only showing the essential features to the user
class Car():
def __init__(self):
self.clutch = False
self.brr = False
self.acc = False
def start(self):
self.clutch = True
self.acc = True
print("car is starting ...")
car1 = Car()
car1.start()
# 2. ENCAPSULATION - wrapping data and functions into a single unit (object)
# Private attributes exist to enforce encapsulation.
# They prevent external code from directly modifying sensitive data.
# _attribute → protected (intended for internal use, but still accessible).
# __attribute → private (name-mangled to prevent direct access outside the class).
class BankAccount:
def __init__(self, owner, balance):
self.owner = owner
self.__balance = balance # private attribute
# Public method to access private data
def get_balance(self):
return self.__balance
# Public method to modify private data safely
def deposit(self, amount):
if amount > 0:
self.__balance += amount
else:
print("Deposit must be positive")
def withdraw(self, amount):
if 0 < amount <= self.__balance:
self.__balance -= amount
else:
print("Invalid withdrawal amount")
# Usage
account = BankAccount("Alice", 1000)
print(account.get_balance()) # Access via method
account.deposit(500)
print(account.get_balance())
# print(account.__balance) Error: Attribute not directly accessible by user but can be accessed by internal functions in the class
# done to prevent exposing instance attributes outside the class
# example:
class Person():
__name = "kas" #private attribute
def __hello(self):
print("hello person")
def welcome(self):
self.__hello()
p1 = Person()
print(p1.welcome())
# 3. INHERITANCE - when one class derives properties and methods of another class
# inherits logic from another class, reduces rewriting of code
class Car():
def __init__(self,type):
self.type = type
color = "pink"
@staticmethod
def start():
print("car is starting...")
@staticmethod
def stop():
print("car is stopping...")
class ToyotaCar(Car): #ToyotaCar inherits from Car class, single level
def __init__(self, brand):
#self.name = name
self.brand = brand
class Fortuner(ToyotaCar): #multi level inheritance
def __init__(self):
self.type = type
super().__init__(type) #super method - used to access methods of the parent class
super.start()
car2 = Fortuner("diesel")
car2.start()
print(car2.type)
#car1=ToyotaCar("fortuner")
#print(car1.start())
#print(car1.color)
#types of inheritance
#1. single inheritance - single parent/base class gives single child/derived class
#2. multi level inheritance - parent class - child class (parent to next class) - grandchild (child to preceding class, containing properties of both preceding classes)
#3. multiple inheritance - child/derived class can inherit properties from multiple parent classes
class A:
varA = "welcome to class A"
class B:
varB = "welcome to class B"
class C(A,B): #multiple inheritance
varC = "welcome to class C"
c1 = C()
print(c1.varA)
print(c1.varB)
print(c1.varC)
# 4. POLYMORPHISM - ex. Operator Overloading
# when same operator is allowed to have different meaning according to the context
print(1+2) #add
print(type(1)) #class int
print('6'+'7') #concatenate
print(type('6') #class string
print([6]+[7]) #merge
print(type([6])) #class list
#for each class the meaning of the operator is defined by python implicitly
#in oops we can define the meaning of any operator as per our requirement using dunder functions (functions starting and ending with __)
class Complex:
def __init__(self, real, img):
self.real = real
self.img = img
def __add__(self, num2): #dunder function
newReal = self.real + num2.real
newImg = self.img + num2.img
return Complex(newReal, newImg)
def __sub__(self, num2): #dunder function
newReal = self.real - num2.real
newImg = self.img - num2.img
return Complex(newReal, newImg)
def show_number(self):
print(self.real, "i +", self.img, "j")
num1 = Complex(1,2)
num1.show_number()
num2 = Complex(3,2)
num2.show_number()
num3 = num1 + num2 #using dunder functions complex numbers can be added, logic is defined
num3.show_number()
num4 = num2 - num1
num4.show_number()