1. Managing data access: private attributes#
All class data in Python is technically public. Any attribute or method of any class can be access by anyone.
However, they are a few ways to manage access to data.
1.1. Naming Convention: internal attributes#
The most important convention is using a single leading
_
–>underscore
to indicate an attribute or method that isn’t a part of the public access interface, and can change without notice.Attention
Nothing is technically preventing you from using these attributes, but a single leading underscore is the developer’s way of saying that you shouldn’t.
1.1.1. Examples - 1#
See some examples below:
obj._att_name obj._method_name()
Note
See that the attribute starts with a single
_
–> “internal”. That means: ” don’t touch this “
1.2. Naming Convention: pseudoprivate attributes#
Another naming convention is using a leading
__
–>double underscore
. Attributes and methods whose names start with__
are the closest thing Python has to “private” fields and methods of other programming languages.The main use of these pseudo-private attributes is to prevent name clashes in child classes. It is a way to protect important attributes and methods that should not be overridden.
1.2.1. Examples - 2#
See some examples below:
obj.__att_name # ---> This is interpreted as `obj.Myclass__attr_name` obj.__method_name()
Note
See that the attribute starts with
__
–> “private”. That means: important attrivutes and methods that should not be overridden.Warning
Leading and trailing
__
are only used for built-in Python methods__init__()
__repr__()
1.2.2. Example - 3#
class BetterDate:
_MAX_DAYS = 30
_MAX_MONTHS = 12
def __init__(self, year, month, day):
self.year, self.month, self.day = year, month, day
@classmethod
def from_str(cls, datestr):
year, month, day = map(int, datestr.split("-"))
return cls(year, month, day)
# Add _is_valid() checking day and month values
def _is_valid(self):
return (self.day <= BetterDate._MAX_DAYS) and \
(self.month <= BetterDate._MAX_MONTHS)
bd1 = BetterDate(2020, 4, 30)
print(bd1._is_valid())
bd2 = BetterDate(2020, 6, 45)
print(bd2._is_valid())
1.3. Use @property to customize access#
How can we control attribute access? or check the value? or make attributes read-only?
1.3.1. Example - 1 property#
class Employee:
def __init__(self, name, new_salary):
self._salary = new_salary #--> use "protected" attribute with leading `_`
@property #--> use `@property` on a method whose name is
def salary(self): # exactly the name of the restricted attribute;
return self._salary
@salary.setter #--> use `@attr.setter on a method `attr() that
def salary(self, new_salary): # will be called on `obj.attr = value`
if new_salary < 0 :
raise ValueError("Invalid salary. Cool right? this value is now protected =) ")
self._salary = new_salary
emp = Employee("Fernando",100000)
print(emp.salary)
emp.salary = -35000 # --> @salary.setter
1.3.2. Example - 2 property#
class Customer:
def __init__(self, name, new_bal):
self.name = name
if new_bal < 0:
raise ValueError("Invalid balance!")
self._balance = new_bal
# Add a decorated balance() method returning _balance
@property
def balance(self):
return self._balance
# Add a setter balance() method
@balance.setter
def balance(self, new_bal):
# Validate the parameter value
if new_bal < 0:
raise ValueError("Invalid balance!")
self._balance = new_bal
print("Setter method called")
# Create a Customer
cust = Customer("Belinda Lutz", 2000)
# Assign 3000 to the balance property
cust.balance = 3000
# Print the balance property
print(cust.balance)