|
1083 | 1083 | "cell_type": "markdown",
|
1084 | 1084 | "metadata": {},
|
1085 | 1085 | "source": [
|
1086 | | - "## **Abstraction**" |
| 1086 | + "## **Abstraction**\n", |
| 1087 | + "Abstraction is the process of **hiding the implementation details** and **exposing only the relevant functionality** to the user.\n", |
| 1088 | + "\n", |
| 1089 | + "In Python, abstraction is implemented using the **abc module**.\n", |
| 1090 | + "\n", |
| 1091 | + "```python\n", |
| 1092 | + "from abc import ABC, abstractmethod\n", |
| 1093 | + "```\n", |
| 1094 | + "- **ABC:** Abstract Base Class\n", |
| 1095 | + "- **@abstractmethod:** Decorator to define abstract methods (methods without implementation)\n", |
| 1096 | + "\n", |
| 1097 | + "| Concept | Purpose |\n", |
| 1098 | + "| ----------------- | ------------------------------------------------ |\n", |
| 1099 | + "| `ABC` | Create abstract base classes |\n", |
| 1100 | + "| `@abstractmethod` | Force child classes to implement certain methods |\n", |
| 1101 | + "\n", |
| 1102 | + "\n", |
| 1103 | + "#### **Why Use Abstraction?**\n", |
| 1104 | + "1. Forces subclasses to implement specific methods.\n", |
| 1105 | + "2. Defines a clear contract/interface for developers.\n", |
| 1106 | + "3. Ensures consistency across different implementations.\n", |
| 1107 | + "4. Reduces coupling between components.\n", |
| 1108 | + "\n", |
| 1109 | + "#### **What Abstraction is not?**\n", |
| 1110 | + "Not just hiding variables with `__` (**that’s encapsulation i.e. hiding internal data/state**)\n" |
1087 | 1111 | ]
|
1088 | 1112 | },
|
1089 | 1113 | {
|
1090 | 1114 | "cell_type": "code",
|
1091 | | - "execution_count": 36, |
| 1115 | + "execution_count": 4, |
1092 | 1116 | "metadata": {
|
1093 | 1117 | "ExecuteTime": {
|
1094 | 1118 | "end_time": "2018年05月31日T17:28:10.195940Z",
|
1095 | 1119 | "start_time": "2018年05月31日T17:28:10.183162Z"
|
1096 | 1120 | }
|
1097 | 1121 | },
|
| 1122 | + "outputs": [], |
| 1123 | + "source": [ |
| 1124 | + "from abc import ABC, abstractmethod\n", |
| 1125 | + "\n", |
| 1126 | + "class Animal(ABC):\n", |
| 1127 | + " \n", |
| 1128 | + " @abstractmethod\n", |
| 1129 | + " def make_sound(self):\n", |
| 1130 | + " pass\n", |
| 1131 | + "\n", |
| 1132 | + " def sleep(self):\n", |
| 1133 | + " print(\"Sleeping...\")\n", |
| 1134 | + "\n", |
| 1135 | + "# This will throw an error:\n", |
| 1136 | + "# animal = Animal()\n", |
| 1137 | + "\n", |
| 1138 | + "class Dog(Animal):\n", |
| 1139 | + " def make_sound(self):\n", |
| 1140 | + " print(\"Woof!\")\n", |
| 1141 | + "\n", |
| 1142 | + "class Cat(Animal):\n", |
| 1143 | + " def make_sound(self):\n", |
| 1144 | + " print(\"Meow!\")" |
| 1145 | + ] |
| 1146 | + }, |
| 1147 | + { |
| 1148 | + "cell_type": "code", |
| 1149 | + "execution_count": 5, |
| 1150 | + "metadata": {}, |
1098 | 1151 | "outputs": [
|
1099 | 1152 | {
|
1100 | | - "data": { |
1101 | | - "text/plain": [ |
1102 | | - "'\\nProcess of highlighting the set of services & hiding the implementation\\nis called ABSTRACTION.\\n'" |
1103 | | - ] |
1104 | | - }, |
1105 | | - "execution_count": 36, |
1106 | | - "metadata": {}, |
1107 | | - "output_type": "execute_result" |
| 1153 | + "name": "stdout", |
| 1154 | + "output_type": "stream", |
| 1155 | + "text": [ |
| 1156 | + "Woof!\n", |
| 1157 | + "Sleeping...\n" |
| 1158 | + ] |
1108 | 1159 | }
|
1109 | 1160 | ],
|
1110 | 1161 | "source": [
|
1111 | | - "# Abstraction means HIDING\n", |
1112 | | - "'''\n", |
1113 | | - "Process of highlighting the set of services & hiding the implementation\n", |
1114 | | - "is called ABSTRACTION.\n", |
1115 | | - "'''" |
| 1162 | + "d = Dog()\n", |
| 1163 | + "\n", |
| 1164 | + "d.make_sound()\n", |
| 1165 | + "d.sleep()" |
| 1166 | + ] |
| 1167 | + }, |
| 1168 | + { |
| 1169 | + "cell_type": "markdown", |
| 1170 | + "metadata": {}, |
| 1171 | + "source": [ |
| 1172 | + "#### **Observation**\n", |
| 1173 | + "1. You can't instantiate Animal directly.\n", |
| 1174 | + "2. Any subclass must implement make_sound(), or it'll raise an error.\n", |
| 1175 | + "3. Common behavior like sleep() can still be implemented in the base class." |
| 1176 | + ] |
| 1177 | + }, |
| 1178 | + { |
| 1179 | + "cell_type": "markdown", |
| 1180 | + "metadata": {}, |
| 1181 | + "source": [ |
| 1182 | + "#### **Real Time Example of Abstraction**\n", |
| 1183 | + "\n", |
| 1184 | + "You’re building a payment system for an edtech platform that allows students to purchase courses using UPI (Unified Payments Interface). But students use different UPI apps — PhonePe, Google Pay, Paytm, BHIM, etc.\n", |
| 1185 | + "\n", |
| 1186 | + "Without Abstraction, you’d have scattered logic, tightly coupling your app with every UPI provider’s implementation. \n", |
| 1187 | + "Whereas, with abstraction: \n", |
| 1188 | + "- You define a common interface for any UPI-based payment processor.\n", |
| 1189 | + "- Each UPI app then implements the common logic behind the scenes, hiding differences.\n", |
| 1190 | + "\n", |
| 1191 | + "Let's do the implementation in three steps:\n", |
| 1192 | + "1. Abstract Class (Interface)\n", |
| 1193 | + "2. Concrete Implementations\n", |
| 1194 | + "3. Usage Layer" |
| 1195 | + ] |
| 1196 | + }, |
| 1197 | + { |
| 1198 | + "cell_type": "code", |
| 1199 | + "execution_count": 6, |
| 1200 | + "metadata": {}, |
| 1201 | + "outputs": [], |
| 1202 | + "source": [ |
| 1203 | + "# Step 1: Abstract Class i.e. UPIPaymentProcessor\n", |
| 1204 | + "\n", |
| 1205 | + "from abc import ABC, abstractmethod\n", |
| 1206 | + "\n", |
| 1207 | + "class UPIPaymentProcessor(ABC):\n", |
| 1208 | + " \n", |
| 1209 | + " @abstractmethod\n", |
| 1210 | + " def pay(self, user_id: str, amount: float, upi_id: str):\n", |
| 1211 | + " \"\"\"Process a UPI payment\"\"\"\n", |
| 1212 | + " pass\n", |
| 1213 | + "\n", |
| 1214 | + " @abstractmethod\n", |
| 1215 | + " def generate_receipt(self) -> str:\n", |
| 1216 | + " \"\"\"Generate transaction receipt\"\"\"\n", |
| 1217 | + " pass" |
| 1218 | + ] |
| 1219 | + }, |
| 1220 | + { |
| 1221 | + "cell_type": "code", |
| 1222 | + "execution_count": 7, |
| 1223 | + "metadata": {}, |
| 1224 | + "outputs": [], |
| 1225 | + "source": [ |
| 1226 | + "# Step 2: Concrete Implementations\n", |
| 1227 | + "\n", |
| 1228 | + "# PhonePe\n", |
| 1229 | + "class PhonePeProcessor(UPIPaymentProcessor):\n", |
| 1230 | + " def pay(self, user_id, amount, upi_id):\n", |
| 1231 | + " print(f\"[PhonePe] Sending ₹{amount} request to {upi_id} for User {user_id}\")\n", |
| 1232 | + " self.transaction_id = f\"PP_{user_id}_TXN001\"\n", |
| 1233 | + " \n", |
| 1234 | + " def generate_receipt(self):\n", |
| 1235 | + " return f\"[PhonePe] Receipt #{self.transaction_id}\"\n", |
| 1236 | + "\n", |
| 1237 | + "\n", |
| 1238 | + "# GooglePay\n", |
| 1239 | + "class GooglePayProcessor(UPIPaymentProcessor):\n", |
| 1240 | + " def pay(self, user_id, amount, upi_id):\n", |
| 1241 | + " print(f\"[GPay] Processing ₹{amount} to {upi_id} for User {user_id}\")\n", |
| 1242 | + " self.transaction_id = f\"GPay_{user_id}_TXN002\"\n", |
| 1243 | + " \n", |
| 1244 | + " def generate_receipt(self):\n", |
| 1245 | + " return f\"[GPay] Receipt #{self.transaction_id}\"\n", |
| 1246 | + "\n", |
| 1247 | + "\n", |
| 1248 | + "# Paytm\n", |
| 1249 | + "class PaytmProcessor(UPIPaymentProcessor):\n", |
| 1250 | + " def pay(self, user_id, amount, upi_id):\n", |
| 1251 | + " print(f\"[Paytm] ₹{amount} sent to {upi_id} for User {user_id}\")\n", |
| 1252 | + " self.transaction_id = f\"PTM_{user_id}_TXN003\"\n", |
| 1253 | + " \n", |
| 1254 | + " def generate_receipt(self):\n", |
| 1255 | + " return f\"[Paytm] Receipt #{self.transaction_id}\"\n" |
| 1256 | + ] |
| 1257 | + }, |
| 1258 | + { |
| 1259 | + "cell_type": "code", |
| 1260 | + "execution_count": 8, |
| 1261 | + "metadata": {}, |
| 1262 | + "outputs": [], |
| 1263 | + "source": [ |
| 1264 | + "# Step 3: Usage Layer\n", |
| 1265 | + "\n", |
| 1266 | + "def checkout(processor: UPIPaymentProcessor, user_id: str, amount: float, upi_id: str):\n", |
| 1267 | + " processor.pay(user_id, amount, upi_id)\n", |
| 1268 | + " receipt = processor.generate_receipt()\n", |
| 1269 | + " print(receipt)" |
| 1270 | + ] |
| 1271 | + }, |
| 1272 | + { |
| 1273 | + "cell_type": "code", |
| 1274 | + "execution_count": 10, |
| 1275 | + "metadata": {}, |
| 1276 | + "outputs": [ |
| 1277 | + { |
| 1278 | + "name": "stdout", |
| 1279 | + "output_type": "stream", |
| 1280 | + "text": [ |
| 1281 | + "[PhonePe] Sending ₹999.0 request to user@ibl for User U101\n", |
| 1282 | + "[PhonePe] Receipt #PP_U101_TXN001\n" |
| 1283 | + ] |
| 1284 | + } |
| 1285 | + ], |
| 1286 | + "source": [ |
| 1287 | + "# Run various payment processors\n", |
| 1288 | + "checkout(PhonePeProcessor(), \"U101\", 999.0, \"user@ibl\")" |
| 1289 | + ] |
| 1290 | + }, |
| 1291 | + { |
| 1292 | + "cell_type": "code", |
| 1293 | + "execution_count": 11, |
| 1294 | + "metadata": {}, |
| 1295 | + "outputs": [ |
| 1296 | + { |
| 1297 | + "name": "stdout", |
| 1298 | + "output_type": "stream", |
| 1299 | + "text": [ |
| 1300 | + "[GPay] Processing ₹499.0 to user@okhdfcbank for User U102\n", |
| 1301 | + "[GPay] Receipt #GPay_U102_TXN002\n" |
| 1302 | + ] |
| 1303 | + } |
| 1304 | + ], |
| 1305 | + "source": [ |
| 1306 | + "checkout(GooglePayProcessor(), \"U102\", 499.0, \"user@okhdfcbank\")" |
| 1307 | + ] |
| 1308 | + }, |
| 1309 | + { |
| 1310 | + "cell_type": "code", |
| 1311 | + "execution_count": 12, |
| 1312 | + "metadata": {}, |
| 1313 | + "outputs": [ |
| 1314 | + { |
| 1315 | + "name": "stdout", |
| 1316 | + "output_type": "stream", |
| 1317 | + "text": [ |
| 1318 | + "[Paytm] ₹799.0 sent to user@paytm for User U103\n", |
| 1319 | + "[Paytm] Receipt #PTM_U103_TXN003\n" |
| 1320 | + ] |
| 1321 | + } |
| 1322 | + ], |
| 1323 | + "source": [ |
| 1324 | + "checkout(PaytmProcessor(), \"U103\", 799.0, \"user@paytm\")" |
1116 | 1325 | ]
|
1117 | 1326 | },
|
1118 | 1327 | {
|
|
1339 | 1548 | "cell_type": "markdown",
|
1340 | 1549 | "metadata": {},
|
1341 | 1550 | "source": [
|
1342 | | - "#### **Real Time Example**\n", |
| 1551 | + "#### **Real Time Example of Encapsulation**\n", |
1343 | 1552 | "\n",
|
1344 | 1553 | "Consider the example of a geographical system that needs to deal with coordinates. There is only a certain range of values for which latitude and longitude make sense. Outside of those values, a coordinate cannot exist. We can create an object to represent a coordinate, but in doing so we must ensure that the values for latitude are at all times within the acceptable ranges. And for this, we can use properties:"
|
1345 | 1554 | ]
|
|
1377 | 1586 | " self._longitude = long_value"
|
1378 | 1587 | ]
|
1379 | 1588 | },
|
1380 | | - { |
1381 | | - "cell_type": "markdown", |
1382 | | - "metadata": {}, |
1383 | | - "source": [ |
1384 | | - "#### **Private Method**" |
1385 | | - ] |
1386 | | - }, |
1387 | | - { |
1388 | | - "cell_type": "code", |
1389 | | - "execution_count": 41, |
1390 | | - "metadata": { |
1391 | | - "ExecuteTime": { |
1392 | | - "end_time": "2018年05月31日T17:41:40.583150Z", |
1393 | | - "start_time": "2018年05月31日T17:41:40.567487Z" |
1394 | | - } |
1395 | | - }, |
1396 | | - "outputs": [ |
1397 | | - { |
1398 | | - "name": "stdout", |
1399 | | - "output_type": "stream", |
1400 | | - "text": [ |
1401 | | - "Updating Software....\n", |
1402 | | - "Driving\n" |
1403 | | - ] |
1404 | | - } |
1405 | | - ], |
1406 | | - "source": [ |
1407 | | - "class Car:\n", |
1408 | | - " def __init__(self):\n", |
1409 | | - " self.__updateSoftware()\n", |
1410 | | - " \n", |
1411 | | - " def drive(self):\n", |
1412 | | - " print('Driving')\n", |
1413 | | - " \n", |
1414 | | - " def __updateSoftware(self):\n", |
1415 | | - " print('Updating Software....')\n", |
1416 | | - " \n", |
1417 | | - "myCar = Car()\n", |
1418 | | - "myCar.drive()\n", |
1419 | | - "\n", |
1420 | | - "mycar.__updateSoftware()" |
1421 | | - ] |
1422 | | - }, |
1423 | 1589 | {
|
1424 | 1590 | "cell_type": "markdown",
|
1425 | 1591 | "metadata": {},
|
|
0 commit comments