forked from huangsam/ultimate-python
- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathencapsulation.py
More file actions
Latest commit
131 lines (108 loc) · 4.66 KB
/
Copy pathencapsulation.py
File metadata and controls
131 lines (108 loc) · 4.66 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
"""
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.
"""
importsecrets
# Module-level constant
_INVALID_AMOUNT_MESSAGE="Invalid amount."
_INSUFFICIENT_BALANCE_MESSAGE="Insufficient balance."
classBankAccount:
def__init__(self, account_holder_name: str) ->None:
"""
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
defdeposit(self, balance: int) ->None:
"""
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)
defwithdraw(self, balance: int) ->None:
"""
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.
"""
ifbalance<=0:
raiseValueError(_INVALID_AMOUNT_MESSAGE)
ifbalance>self.__balance:
raiseValueError(_INSUFFICIENT_BALANCE_MESSAGE)
self.__balance-=balance
defget_balance(self) ->int:
"""
This function returns the available balance in the account.
"""
returnself.__balance
defget_account_number(self) ->int:
"""
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.
"""
returnself.__account_number
def__set_account_number(self, number: int) ->None:
"""
This is a private method. Similar to private variables,
private methods also cannot be accessed outside the class.
"""
self.__account_number=number
defremove_account_details(self) ->None:
"""
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=""
defmain() ->None:
# Account names constants.
user1="John Doe"
user2="Jane Doe"
# Account instances.
account1=BankAccount(user1)
account2=BankAccount(user2)
assertaccount1.account_holder_name==user1
assertaccount2.account_holder_name==user2
# Deposit amount and check if the balance is updated.
account1.deposit(100)
assertaccount1.get_balance() ==100
# Withdraw amount and check if the balance is updated.
account1.withdraw(50)
assertaccount1.get_balance() ==50
# validating invalid amounts.
error_inputs= [-10, 0, 150]
forinputinerror_inputs:
try:
account1.withdraw(input)
exceptValueErrorase:
assertstr(e) in {_INSUFFICIENT_BALANCE_MESSAGE, _INVALID_AMOUNT_MESSAGE}
# Remove account details and assert values.
account1.remove_account_details()
assertaccount1.get_balance() ==0
assertaccount1.get_account_number() ==0
assertaccount1.account_holder_name==""
if__name__=="__main__":
main()