-
-
Notifications
You must be signed in to change notification settings - Fork 519
/
encapsulation.py
130 lines (108 loc) · 4.58 KB
/
encapsulation.py
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
"""
Encapsulation is a feature of OOP that allows you to hide the
implementation details of a class from its users.
Encapsulation allows us to limit the access of certain attributes
within a class. This prevents users from directly accessing and modifying such
attributes from outside the class. Instead, users must use methods to access and
modify attributes.
"""
import secrets
# Module-level constant
_INVALID_AMOUNT_MESSAGE = "Invalid amount."
_INSUFFICIENT_BALANCE_MESSAGE = "Insufficient balance."
class BankAccount:
def __init__(self, account_holder_name):
"""
In Python, a class attribute can be made private by prefixing it with two underscores.
This makes it inaccessible to users outside the class.
By default, class attributes are public. Therefore, they can be accessed and modified
outside the class.
Here, account_number and balance are private while account_holder_name is public.
"""
self.account_holder_name = account_holder_name
"""
The account number is generated automatically using the randbelow function from
the random module when a new instance of the class is created.
The balance is set to 0 by default.
"""
self.__account_number = secrets.randbelow(10**10) # generate a random account number of 10 digits.
self.__balance = 0
def deposit(self, balance):
"""
The deposit function is used to add new balance to the account.
The provided balance is added to the existing balance.
"""
self.__balance += int(balance)
def withdraw(self, balance):
"""
The withdrawal method is used to deduct the balance from the account.
In case there is insufficient balance, or the input is invalid,
a value error is raised.
"""
if balance <= 0:
raise ValueError(_INVALID_AMOUNT_MESSAGE)
if balance > self.__balance:
raise ValueError(_INSUFFICIENT_BALANCE_MESSAGE)
self.__balance -= balance
def get_balance(self):
"""
This function returns the available balance in the account.
"""
return self.__balance
def get_account_number(self):
"""
The account number is generated randomly when a new instance of the class is created.
Since the attribute is also private, it cannot be accessed directly from outside the class.
The get_account_number method allows you to access the account number outside the class.
But since we do not define a setter method for this variable, we cannot modify it outside the class.
Therefore, the account number generated while creating an object of the BankAccount class cannot be changed
but can only be read using this function.
"""
return self.__account_number
def __set_account_number(self, number):
"""
This is a private method. Similar to private variables,
private methods also cannot be accessed outside the class.
"""
self.__account_number = number
def remove_account_details(self):
"""
This method is used to reset the account details.
Here, the __set_account_number function is private.
This, it cannot be called from outside the class.
However, the remove_account_details calls the function from
inside the class and as it is a public method, it can be called from
outside the class.
"""
self.__balance = 0
self.__set_account_number(0)
self.account_holder_name = ""
def main():
# Account names constants.
user1 = "John Doe"
user2 = "Jane Doe"
# Account instances.
account1 = BankAccount(user1)
account2 = BankAccount(user2)
assert account1.account_holder_name == user1
assert account2.account_holder_name == user2
# Deposit amount and check if the balance is updated.
account1.deposit(100)
assert account1.get_balance() == 100
# Withdraw amount and check if the balance is updated.
account1.withdraw(50)
assert account1.get_balance() == 50
# validating invalid amounts.
error_inputs = [-10, 0, 150]
for input in error_inputs:
try:
account1.withdraw(input)
except ValueError as e:
assert str(e) in {_INSUFFICIENT_BALANCE_MESSAGE, _INVALID_AMOUNT_MESSAGE}
# Remove account details and assert values.
account1.remove_account_details()
assert account1.get_balance() == 0
assert account1.get_account_number() == 0
assert account1.account_holder_name == ""
if __name__ == "__main__":
main()