16 OOP 3: Encapsulation, Abstraction

image.png Երևան, Մոսկովյան, լուսանկարի հղումը, Հեղինակ՝ Taguhi Kirakosyan

Open In Colab (ToDo)

Song reference - ToDo

🎦 Տեսադասեր + լրացուցիչ (ToDo)

ToDo 1. Տեսություն 2025
2. Տեսություն 2023 (ToDo)
3. Գործնական 2025
4. Գործնական 2023 (ToDo)
5. Որոշ տնայինների լուծումներ (ToDo)

Google Forms ToDo

📚 Նյութը

Encapsulation (նույն ինքը՝ “քեզ ինչ”)

https://www.datacamp.com/tutorial/encapsulation-in-python-object-oriented-programming

image.png

image.png
Feature Python Java C++
Access modifiers No enforced modifiers; uses naming conventions (_protected, __private) Enforced with public, protected, private keywords Enforced with public, protected, private keywords
Getter/setter methods Optional, often used with @property decorator for controlled access Common practice, typically implemented as methods Common practice, typically implemented as methods
Data Access Accessible via naming conventions; relies on developer discipline Controlled by access modifiers; enforced by the compiler Controlled by access modifiers; enforced by the compiler
Philosophy “We are all adults here” - relies on conventions and trust Strict enforcement of access control Strict enforcement of access control
class Avto:
    def __init__(self, model="06", owner="Մեքենա մեքենիկյան", secret="չեմ ասի"):
        self.model = model # public
        self._owner = owner # protected
        self.__secret = secret # private

        print(self.model, self._owner, self.__secret)

Public, Protected, Private

Level Prefix Enforcement Use Case
Public none None Functions & data meant for everyone
Protected _single Convention Internal helpers, subclass support
Private __double Name-mangling Strictly internal, avoid subclass clashes

Public

jiguli = Avto()

print(jiguli.model)

jiguli.model = "07"

print(jiguli.model)
06 Մեքենա մեքենիկյան չեմ ասի
06
07

Protected

jiguli = Avto()

print(jiguli._owner)

jiguli._owner = ""

print(jiguli._owner)
06 Մեքենա մեքենիկյան չեմ ասի
Մեքենա մեքենիկյան

Private (name mangling)

ոնց-որ թե հազիվ ձև գտանք, բայց իրականում նույն չափ էֆեկտիվ մեթոդ ա ինչ հայտարարելը “շուտ եմ ասել, ով էս փոփոխականի հետ գործ արեց փիղ ա”

jiguli = Avto()

print(jiguli.__secret)

# jiguli.__secret = "Մեքենայատեր Տերյան"

# print(jiguli.__secret)
06 Մեքենա մեքենիկյան չեմ ասի
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [5], in <cell line: 3>()
      1 jiguli = Avto()
----> 3 print(jiguli.__secret)
      5 # jiguli.__secret = "Մեքենայատեր Տերյան"
      6 
      7 # print(jiguli.__secret)

AttributeError: 'Avto' object has no attribute '__secret'
dir(jiguli)
['_Avto__secret',
 '__class__',
 '__delattr__',
 '__dict__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__',
 '__weakref__',
 '_owner',
 'model']
jiguli = Avto()

print(jiguli._Avto__secret)

jiguli._Avto__secret = "Մեքենայատեր Տերյան"

print(jiguli._Avto__secret)
06 Մեքենա մեքենիկյան չեմ ասի
չեմ ասի
Մեքենայատեր Տերյան

Getter, setter, deleter-ներ

Մենք ուզում ենք ինչքան հնարավորա ուրիշներին ասենք հեռու մնացեք մեր կոդից, ձեզ մենակ էս-էս բաներն ենք տրամադրում

եթե ուզում են ինչ-որ փոփոպականի կպնեն ու հետը փոփոխություն անեն ինչ-որ ոչ թե թույլ կտանք ուղիղ հենց իրա հետ գործ անեն ու ինչ-որ բան ջարդեն կամ տեսնեն ինչ-որ բան որն իրանց աչքերի համար չէր, այլ ասենք ուզում ես արժեքը ստանաս, խնդիր չկա կանչի էս ֆունկցիան՝ կտա, ուզում ես արժեք փոխես (ու սխալ մուտք տաս կոդը ջարդես), տուր արժեքդ մենք կփոխենք

class Avto:
    def __init__(self, model="06", owner="Մեքենա մեքենիկյան", secret="չեմ ասի"):
        self.model = model
        self._owner = owner
        self.__secret = secret

    def info(self):
        text = f"{self.model} {self._owner.capitalize()}, {self.__secret}"
        return text
jiguli = Avto()

print(jiguli.info())

print(jiguli._owner)

jiguli._owner = 1

print(jiguli.info())
06 Մեքենա մեքենիկյան, չեմ ասի
Մեքենա մեքենիկյան
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [9], in <cell line: 9>()
      5 print(jiguli._owner)
      7 jiguli._owner = 1
----> 9 print(jiguli.info())

Input In [8], in Avto.info(self)
      7 def info(self):
----> 8     text = f"{self.model} {self._owner.capitalize()}, {self.__secret}"
      9     return text

AttributeError: 'int' object has no attribute 'capitalize'
class Avto:
    def __init__(self, model="06", owner="Մեքենա մեքենիկյան", secret="չեմ ասի"):
        self.model = model
        self._owner = owner
        self.__secret = secret

    def info(self):
        text = f"{self.model} {self._owner.capitalize()}, {self.__secret}"
        return text
    
    def get_owner(self):
        return self._owner

    def set_owner(self, value):
        if isinstance(value, str):
            self._owner = value
        else:
            # self._owner = str(value)
            raise ValueError("Անուն տուր ոչ թե թիվ")

    def del_owner(self):
        print("Մեքենայի տիրոջ անունը ջնջում եմ")
        del self._owner
jiguli = Avto()

print(jiguli.info())

print(jiguli.get_owner())

jiguli.set_owner(1)

# # jiguli.set_owner("Ավտո Վուվույան")

# print(jiguli.info())

# jiguli.del_owner()

# print(jiguli.info())
06 Մեքենա մեքենիկյան, չեմ ասի
Մեքենա մեքենիկյան
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Input In [12], in <cell line: 7>()
      3 print(jiguli.info())
      5 print(jiguli.get_owner())
----> 7 jiguli.set_owner(1)
      8 # # jiguli.set_owner("Ավտո Վուվույան")
      9 
     10 # print(jiguli.info())
   (...)
     13 
     14 # print(jiguli.info())

Input In [10], in Avto.set_owner(self, value)
     16     self._owner = value
     17 else:
     18     # self._owner = str(value)
---> 19     raise ValueError("Անուն տուր ոչ թե թիվ")

ValueError: Անուն տուր ոչ թե թիվ

Property decorator

Բայց ախր շատ անհարմարա սենց ֆունկցիաների անուններ գրել երկար ամեն անգամ ֆունկցիա աշխատացնել, լավ էր էլի որ գրում էինք ուղղակի jiguli._owner = ․․

jiguli.set_owner("aaa")
jiguli._owner = "aaa"
class Avto:
    def __init__(self, model="06", owner="Մեքենա մեքենիկյան", secret="չեմ ասի"):
        self.model = model
        self._owner = owner
        self.__secret = secret

    def info(self):
        text = f"{self.model}"
        return text
    
    @property
    def owner(self):
        return self._owner

    @owner.setter
    def owner(self, value):
        if isinstance(value, str):
            self._owner = value
        else:
            raise ValueError("անուն տուր ոչ թե թիվ")

    @owner.deleter
    def owner(self):
        print("ջնջում եմ")
        del self._owner
jiguli = Avto()

print(jiguli.info())

print(jiguli.owner)  # Using getter
06
Մեքենա մեքենիկյան
jiguli.owner = "Ավտո Վուվույան"  # Using setter # jiguli.set_owner("Ավտո ․․․")
del jiguli.owner # jiguli.delete_owner()
ջնջում եմ
del jiguli.model

Եթե owner-ը protected չլիներ։

class Avto:
    def __init__(self, model="06", owner="Մեքենա մեքենիկյան", secret="չեմ ասի"):
        self.model = model
        self._owner = owner
        self.__secret = secret

    @property
    def owner(self):
        return self.owner
    
    def _gaxtni_info(self):
        
jiguli = Avto() #յիգւլի․owner

jiguli._owner = 3
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Input In [11], in <cell line: 3>()
      1 jiguli = Avto() #յիգւլի․owner
----> 3 jiguli.owner = 3

AttributeError: can't set attribute 'owner'

Abstraction

Abstraction is the process of hiding the complex reality while exposing only the necessary parts. In code, it lets you:

  • Simplify by focusing on “what” something does, not “how” it does it.
  • Encapsulate complexity behind a clean interface.
  • Promote reuse and flexibility by decoupling implementation from usage.
import os

class FileReader:
    def __init__(self, path):
        self.path = path
        self.size = self.get_size()
        self.text = None

    def get_size(self):
        return os.stat(self.path).st_size

    def read(self):
        # pass
        raise NotImplementedError

    def write(self):
        # pass
        raise NotImplementedError
class TextReader(FileReader):
    def read(self):
        if self.text is None:
            with open(self.path, "r") as f:
                self.text = f.read()

        return self.text
with open("a.txt", "w") as f:
    f.write("Շուդադի շուդադա վիզաութ յո լավ, վեդեֆոյու, վեդեֆոյու")
t = TextReader("a.txt")
t.get_size()
96
t.write()
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
Input In [18], in <cell line: 1>()
----> 1 t.write()

Input In [13], in FileReader.write(self)
     16 def write(self):
     17     # pass
---> 18     raise NotImplementedError

NotImplementedError: 

ուզում ենք աշխատացնենք write-ը, TextReader-ը չունի, գնում ա կանչում ա FileReader-ինը իսկ ընդեղ գրած ա որ էռոռ ա բարձրացնում որովհետև գրված չի մեթոդի կոդը

Լավ կլիներ բայց որ եթե writeը ֆունդամենտալ մասա կլասի, ու առանց իրա պետքա կլասը գոյություն ունենա նենց անենք որ կլասից ժառանգողը ստիպված լինի սահմանի էդ մեթոդը

Abstract methods

from abc import ABC, abstractmethod

# ձևանմուշ (template) 
class FileReader(ABC): # Abstract Base Class
    def __init__(self, path):
        self.path = path
        self.size = self.get_size()
        self.text = None

    def get_size(self):
        return os.stat(self.path).st_size

    @abstractmethod
    def read(self):
        pass

    @abstractmethod
    def write(self):
        pass
class TextReader(FileReader):
    def read(self):
        if self.text is None:
            with open(self.path, "r") as f:
                self.text = f.read()

        return self.text
t = TextReader("a.txt")
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [26], in <cell line: 1>()
----> 1 t = TextReader("a.txt")

TypeError: Can't instantiate abstract class TextReader with abstract method write
class TextReader(FileReader):
    def read(self):
        if self.text is None:
            with open(self.path, "r") as f:
                self.text = f.read()

        return self.text

    def write(self):
        # pass
        print("էլ չի ջարդվում կոդը")
t = TextReader("a.txt")

t.write()
էլ չի ջարդվում կոդը
# class JsonReader
class Model: # algorithm
    @abstractmethod
    def read_data # fit
    
    @abstractmethod
    def predict():
        pass
    
    # .fit 
    # .predict

Abstract variables


class FileReader(ABC):
    def __init__(self, path):
        self.path = path
        self.size = self.get_size()
        self.text = None

    @property
    @abstractmethod
    def description(self):
        pass

    def get_size(self):
        return os.stat(self.path).st_size

    @abstractmethod
    def read(self):
        pass

    @abstractmethod
    def write(self):
        pass
class TextReader(FileReader):
    
    def read(self):
        if self.text is None:
            with open(self.path, "r") as f:
                self.text = f.read()

        return self.text

    def write(self):
        print("էլ չի ջարդվում կոդը")

    description = "1"
    # @property
    # def description(self):
    #     return "Սևանը չէ՞"
t = TextReader("a.txt")

t.description
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [41], in <cell line: 1>()
----> 1 t = TextReader("a.txt")
      3 t.description

TypeError: Can't instantiate abstract class TextReader with abstract method description

🏡Տնային

🎲 16