Three Types of Access modifiers.
property
decorator.# Create a parent class
class MyClass:
def __init__(self):
# public variable
self.my_var1 = 10
# protected variable
self._my_var2 = 20
# Notice: The '_' underscore before 'my_var2'
# private variable
self.__my_var3 = 30
# Notice: The '__' underscore before 'my_var3'
def demo_method(self):
# can access all variables
print(f"{self.my_var1}, {self._my_var2}, {self.__my_var3}")
# Create child class
class MyClass1(MyClass):
def __init__(self):
super().__init__()
## Access by parent instance
my_instance = MyClass()
print(my_instance.my_var1) # 10
print(my_instance._my_var2) # 20
# accessing private variable
my_instance.demo_method() # 10, 20, 30
# Note: Comment the next line to continue further execution
print(my_instance.__my_var3) # AttributeError
## Access by child instance
my_instance = MyClass1()
print(my_instance.my_var1) # 10
print(my_instance._my_var2) # 20
print(my_instance.__my_var3) # AttributeError
## Example: Define a class with getter and setter methods
class MyClass:
def __init__(self):
# public variable
self.my_var1 = 10
# protected variable
self._my_var2 = 20
# private variable
self.__my_var3 = 30
# 'get_my_var()' is getter method that returns the private variable
def get_my_var(self):
return self.__my_var3
# 'set_my_var()' is a setter method that sets the value
# to a private variable
def set_my_var(self, new_value):
# if you want '__my_var3' read-only you can raise 'AttributeError',
# uncomment the following line
# raise AttributeError("Cannot change this value")
self.__my_var3 = new_value
some_instance = MyClass()
# call the getter method
print(some_instance.get_my_var()) # 30
# call the setter method
some_instance.set_my_var(50)
# check the value with getter again
print(some_instance.get_my_var()) # 50
# but still can't access directly
print(some_instance.__my_var3) # AttributeError
class MyClass:
def __init__(self):
self.my_var1 = 10
self._my_var2 = 20
self.__my_var3 = 30
some_instance = MyClass()
# '__dict__' is a special variable in Python that keeps track of
# attributes of module/class/object,
# you can print '__dict__' to show all variables, including private
print(some_instance.__dict__) # {'my_var1': 10, '_my_var2': 20, \
# '_MyClass__my_var3': 30}
# or use 'vars()' function to return the '__dict__' variable
print(vars(some_instance)) # {'my_var1': 10, '_my_var2': 20, \
# '_MyClass__my_var3': 30}
# this is called name mangling, in which the
# '_CLASSNAME' prefix is used for private variables
## So you can see the private variable right away,
# and now by knowing its name you can alter/remove it using
# the same naming convention
some_instance._MyClass__my_var3 = 42
print(some_instance._MyClass__my_var3) # 42
# can also remove the variable
del some_instance._MyClass__my_var3
# causing 'AttributeError' next
print(some_instance._MyClass__my_var3) # AttributeError
# So in summary it's not really a private variable,
# Python does not follow the concept
Parameters:
str
]: Provide some information about this property.Explanation: It is a Pythonic way to use getters and setters in encapsulation. property()
function simply allows assigning/altering private variables using the '.' operator without really exposing the real (private) variable. Using this function accessing/modifying becomes just as convenient as operating on a regular variable. property()
can also be used as a decorator for further convenience. For more implementation details check Official Python docs.
Descriptor vs Property: Descriptors are the low-level mechanism behind allowing class variables to control what happens during attribute lookup. And properties are descriptors, they are an implementation of descriptors. Although one drawback using descriptors is that for every variable it requires a separate class, which is not the case with properties. One can have multiple variables of such behaviour inside a single class using properties.
## Example: Implement encapsulation using property
class MyClass:
def __init__(self):
# public variable
self.my_var1 = 10
# protected variable
self._my_var2 = 20
# private variable
self.__my_var3 = 30
self.__my_var4 = 40
# Getter method for '__my_var3'
# Note: The property decorator, 'my_var' can be renamed to any other name
@property
def my_var(self):
print("Getter method called")
return self.__my_var3
# Setter method for '__my_var3'
# if you don't want '__my_var3' value to be altered, don't define
# this method
# Notice: The '<VAR_NAME>.setter' decorator
@my_var.setter
def my_var(self, new_value):
print("Setter method called")
self.__my_var3 = new_value
# Deleter method for '__my_var3'
# if you don't want '__my_var3' value to be deleted, don't define
# this method
@my_var.deleter
def my_var(self):
print("Deleter method called")
self.__my_var3 = None
# getter method for '__my_var4',
# this is the second variable (we talked about in property vs descriptors)
@property
def some_var(self):
return self.__my_var4
# or without using property decorator, just pass the functions
# to 'property()' function, example in below comment
# my_var = property(my_getter, my_setter, my_deleter)
# that's it, 'my_var' will have getter, setter, deleter methods
# Note: Now '__my_var3' is a private variable and 'my_var' is the variable
# that can be used to modify '__my_var3' from outside of the class
some_instance = MyClass()
# let's check the values
print(some_instance.__dict__) # {'my_var1': 10, '_my_var2': 20, \
# '_MyClass__my_var3': 30, \
# '_MyClass__my_var4': 40}
## Access using the property variables by '<VAR_NAME>', calls the getter method
print(some_instance.my_var) # Getter method called \
# 30
print(some_instance.some_var) # 40
## Alter their values using '=' operator, calls the setter method
some_instance.my_var = 50 # Setter method called
print(some_instance.my_var) # Getter method called \
# 50
## Delete/reset the value of variable
del some_instance.my_var # Deleter method called
# 'some_var' has no setter/deleter methods so will raise 'AttributeError'
some_instance.some_var = 90 # AttributeError
del some_instance.some_var # AttributeError