Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encapsulation #79

Merged
merged 9 commits into from
Oct 6, 2023
117 changes: 117 additions & 0 deletions ultimatepython/classes/encapsulation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
"""
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 random

class BankAccount:
def __init__(self, account_holder_name: str):
abhinna1 marked this conversation as resolved.
Show resolved Hide resolved
"""
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 variables are public. Therefore, they can be accessed and modified
abhinna1 marked this conversation as resolved.
Show resolved Hide resolved
outside of the class.

Here, account_number and balace are private while account_holder_name is public.
"""
self.account_holder_name: str = account_holder_name
"""
The account number is generated automaticallyusing the randint function from the random module
when a new instance of the class is created.
The balance is set to 0.0 by default.
"""
self.__account_number: int = random.randint(1000000000, 9999999999) # generate a random account number of 10 digits.
abhinna1 marked this conversation as resolved.
Show resolved Hide resolved
self.__balance: float = 0.0

def deposit(self, balance: float):
self.__balance += balance # add the deposited amount to the balance.

def withdraw(self, balance: float):
self.__balance -= balance
huangsam marked this conversation as resolved.
Show resolved Hide resolved

def get_balance(self)-> float:
return self.__balance

def get_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 of 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:int):
"""
This is a private method. Similar to private variables,
private methods also cannot be accessed outside the class.
"""
self.__account_number = number

def delete_account(self):
abhinna1 marked this conversation as resolved.
Show resolved Hide resolved
"""
This method is used to delete an account.
"""
self.__balance = 0.0
self.__set_account_number(0)
self.account_holder_name = ""
def main():
abhinna1 marked this conversation as resolved.
Show resolved Hide resolved
# Account names constants.
USER1: str = "John Doe"
USER2: str = "Jane Doe"

# Account instances.
account1 = BankAccount(USER1)
account2 = BankAccount(USER2)

# Accounts list.
accounts = [account1, account2]

assert account1.account_holder_name == USER1
huangsam marked this conversation as resolved.
Show resolved Hide resolved
assert account2.account_holder_name == USER2

# Check if the accounts are an instance of the BankAccount class.
assert all(isinstance(account, BankAccount) for account in accounts)
# Check if the account balance are floats.
assert all(isinstance(account.get_balance(), float) for account in accounts)
huangsam marked this conversation as resolved.
Show resolved Hide resolved
# Check if the account balance are integers.
assert all(isinstance(account.get_account_number(), int) for account in accounts)

# Deposit amount and check if the balance is updated.
account1.deposit(100.0)
assert(account1.get_balance() == 100.0)

# Withdraw amount and check if the balance is updated.
account1.withdraw(50.0)
assert(account1.get_balance() == 50.0)

# Assert the data types of account balance.
assert(isinstance(account1.get_balance(), float))

"""
Uncommenting the following code will throw an AttributeError referring that the attribute is not defined
since the private attributes cannot be accessed or modified directly from outside the class.
"""
# account1.__balance = 100.0
# assert(account1.__balance, int)
# account2.__account_number = 1234581239
# assert(isinstance(account1.__account_number(), int))
# account1.__set_account_number(1234567890)
# assert(isinstance(account1.__set_account_number, int))
abhinna1 marked this conversation as resolved.
Show resolved Hide resolved


# Delete account and assert values.
huangsam marked this conversation as resolved.
Show resolved Hide resolved
account1.delete_account()
assert(account1.get_balance() == 0.0)
assert(account1.get_account_number() == 0)
assert(account1.account_holder_name == "")

if __name__ == "__main__":
main()