Наследование атрибутов класса питон

Наследование. Атрибуты private и protected

Мы продолжаем изучение темы «наследование». На этом занятии мы увидим, как влияет режим доступа private и protected атрибутов при наследовании классов.

  • _attribute (с одним подчеркиванием) – режим доступа protected (служит для обращения внутри класса и во всех его дочерних классах)
  • __attribute (с двумя подчеркиваниями) – режим доступа private (служит для обращения только внутри класса).
class Geom: name = 'Geom' def __init__(self, x1, y1, x2, y2): print(f"инициализатор Geom для ") self.__x1 = x1 self.__y1 = y1 self.__x2 = x2 self.__y2 = y2 class Rect(Geom): def __init__(self, x1, y1, x2, y2, fill='red'): super().__init__(x1, y1, x2, y2) self.__fill = fill

Здесь мы пытаемся в инициализаторе базового класса Geom сформировать приватные локальные свойства с координатами прямоугольника. Дополнительно в инициализаторе самого класса создается приватное свойство __fill. Ниже создадим объект класса Rect:

После запуска программы увидим следующие строчки: инициализатор Geom для
Смотрите, локальные свойства с координатами имеют префикс _Geom, то есть, префикс того класса, в котором они непосредственно были прописаны. Несмотря на то, что параметр self является ссылкой на объект класса Rect. Это особенность поведения (формирования) приватных атрибутов в базовых классах. У них всегда добавляется префикс именно базового класса, а не класса объекта self. А вот последнее свойство __fill имеет ожидаемый префикс _Rect, так как оно было создано в классе Rect. Что из этого следует? Во-первых, мы, конечно же, не можем обратиться в свойствам-координатам в дочернем классе Rect. Если в нем прописать метод get_coords():

def get_coords(self): return (self.__x1, self.__y1, self.__x2, self.__y2)

то увидим ошибку AttributeError. Но если перенести этот метод в базовый класс Geom, то все сработает без ошибок, так как приватным свойствам будет добавлен правильный префикс _Geom. Возможно, вам кажется это немного запутанным? Но давайте вспомним, а для чего вообще нужны и когда используются приватные атрибуты. Мы говорили, что это закрытые от внешнего вмешательства свойства или методы текущего класса, доступные только внутри этого класса и недоступные из других, в том числе и из дочерних классов. Именно поэтому приватные атрибуты жестко привязываются к текущему классу, в котором они создаются, так как по логике предполагается их использовать только внутри этого класса и больше нигде. Если же нам нужно определить закрытые атрибуты, доступные в текущем классе и во всех его дочерних классах, то для этого следует использовать метод определения protected – одно нижнее подчеркивание. Поэтому правильнее было бы создавать свойства-координаты в базовом инициализаторе в режиме protected:

class Geom: name = 'Geom' def __init__(self, x1, y1, x2, y2): print(f"инициализатор Geom для ") self._x1 = x1 self._y1 = y1 self._x2 = x2 self._y2 = y2 class Rect(Geom): def __init__(self, x1, y1, x2, y2, fill='red'): super().__init__(x1, y1, x2, y2) self._fill = fill def get_coords(self): return (self._x1, self._y1, self._x2, self._y2)
r = Rect(0, 0, 10, 20) print(r.__dict__) r.get_coords()

После запуска программы увидим следующие строчки: инициализатор Geom для
Опять же, как я ранее отмечал, режим доступа protected в реальности никак не ограничивает доступ к атрибутам объектов класса или самого класса. Например, мы можем обратиться к координатам напрямую через экземпляр класса:

Читайте также:  Скрипт определить язык php

Никаких ошибок не будет. Нижнее подчеркивание лишь предупреждает (сигнализирует) программиста о защищенном атрибуте, к которому напрямую лучше не обращаться. Этот атрибут был создан для внутренней логики работы алгоритма в классе и не предназначен для обращения извне. В дальнейшем это может привести к проблемам, например, при изменении версии класса, в котором такого атрибута уже не будет, или он будет играть другую роль и т.п.

Атрибуты private и protected на уровне класса

Все также работает и с атрибутами уровня класса. Например, сейчас мы совершенно спокойно можем обратиться к свойству name класса Geom через объект класса Rect:

Добавив одно нижнее подчеркивание, функционал останется прежним, мы лишь отметим, что к этой переменной извне лучше не обращаться:

class Rect(Geom): def __init__(self, x1, y1, x2, y2, fill='red'): super().__init__(x1, y1, x2, y2) self._fill = fill self._name = self.__name
class Geom: __name = 'Geom' def __init__(self, x1, y1, x2, y2): print(f"инициализатор ") self._x1 = x1 self._y1 = y1 self._x2 = x2 self._y2 = y2

Те же ограничения доступа можно накладывать и на методы. Если в базовом классе Geom определить приватный метод, например, для проверки корректности значений координат:

class Geom: . def __verify_coord(self, coord): return 0  coord  100

то он будет доступен только внутри этого класса и вызвать его, скажем, в дочернем классе Rect уже не получится:

class Rect(Geom): def __init__(self, x1, y1, x2, y2, fill='red'): super().__init__(x1, y1, x2, y2) super().__verify_coord(x1) self._fill = fill

Источник

Наследование в Python

Изображение баннера

Это продолжение статьи «Классы» из раздела «ООП в Python» .

Для лучшего понимания этого материала пригодятся знания из следующих статей: Методы , Класс методы , Статические методы , super() , Декораторы , isinstance()

Наследование (англ. inheritance) — концепция объектно-ориентированного программирования, согласно которой абстрактный тип данных может наследовать данные и функциональность некоторого существующего типа, способствуя повторному использованию компонентов программного обеспечения.

Чтобы один класс наследовал от другого нужно при объявлении класса передать родительский класс как аргумент.

class A : def funcA (self): print ( «A» ) class B (A): def funcB (self): print ( «B» ) b = B() A.funcA = B.funcB b.funcA()

Пример

Создадим класс Employee и производный от него класс Developer.

Developer — наследник Employee. Для начала у Developer не будет функционала — просто убедимся, что у объекта производного класса есть те же атрибуты, что и у родитльского. Также проверим что можно применять методы из родительского класса. В конце изучим класс с помощью help.

class Employee : num_of_emps = 0 raise_amt = 1.04 def __init__ (self, first, last, pay): self.first = first self.last = last self.pay = pay self.email = first + ‘.’ + last + ‘@kaspersky.com’ Employee.num_of_emps += 1 def fullname (self): return f ‘ ‘ def apply_raise (self): self.pay = int (self.pay * self.raise_amt) class Developer (Employee): pass dev_1 = Developer( ‘Eugene’ , ‘Kaspersky’ , 50000 ) dev_2 = Developer( ‘Test’ , ‘User’ , 60000 ) print (dev_1.email) print (dev_2.email) print (dev_1.pay) dev_1.apply_raise() print (dev_1.pay) print ( help (Developer))

Eugene.Kaspersky@kaspersky.com Test.User@kaspersky.com 50000 52000 Help on class Developer in module __main__: class Developer(Employee) | Developer(first, last, pay) | | Method resolution order: | Developer | Employee | builtins.object | | Class methods defined here: | | from_string(emp_str) from builtins.type | | set_raise_amt(amount) from builtins.type | | ———————————————————————- | Static methods defined here: | | is_workday(day) | | ———————————————————————- | Methods inherited from Employee: | | __init__(self, first, last, pay) | Initialize self. See help(type(self)) for accurate signature. | | apply_raise(self) | | fullname(self) | | ———————————————————————- | Data descriptors inherited from Employee: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) | | ———————————————————————- | Data and other attributes inherited from Employee: | | num_of_emps = 2 | | raise_amt = 1.04 (END)

Пример с новыми атрибутами

Добавим классу Developer новый атрибут — язык программирования. Также создадим для этого класса метод __init__()

class Employee : num_of_emps = 0 raise_amt = 1.04 def __init__ (self, first, last, pay): self.first = first self.last = last self.pay = pay self.email = first + ‘.’ + last + ‘@kaspersky.com’ Employee.num_of_emps += 1 def fullname (self): return f ‘ ‘ def apply_raise (self): self.pay = int (self.pay * self.raise_amt) class Developer (Employee): raise_amt = 1.10 def __init__ (self, first, last, pay, prog_lang): super ().__init__(first, last, pay) # also possible # Employee.__init__(self, first, last, pay) self.prog_lang = prog_lang emp_1 = Employee( ‘Andrei’ , ‘Olegovich’ , 40000 ) dev_1 = Developer( ‘Eugene’ , ‘Kaspersky’ , 50000 , ‘Python’ ) dev_2 = Developer( ‘Test’ , ‘User’ , 60000 , ‘Java’ ) print (f «emp_1 raise_amt: » ) # 1.04 print (f «dev_1 raise_amt: » ) # 1.1 print (f «dev_1 Email: » ) print (dev_1.prog_lang) # Python

emp_1 raise_amt: 1.04 dev_1 raise_amt: 1.1 dev_1 Email: Eugene.Kaspersky@kaspersky.com Python

Пример с новыми методами

Теперь создадим класс Manager, у которого будут новые атрибуты и новые методы для работы с ними.

class Manager (Employee): def __init__ (self, first, last, pay, employees= None ): super ().__init__(first, last, pay) if employees is None : self.employees = [] else : self.employees = employees def add_emp (self, emp): if emp not in self.employees: self.employees.append(emp) def remove_emp (self, emp): if emp in self.employees: self.employees.remove(emp) def print_emps (self): for emp in self.employees: print ( ‘—>’ , emp.fullname()) mgr_1 = Manager( ‘Sue’ , ‘Smith’ , 90000 , [dev_1]) mgr_1.print_emps() print (mgr_1.email) mgr_1.add_emp(dev_2) mgr_1.print_emps() print (mgr_1.fullname()) mgr_1.remove_emp(dev_1) mgr_1.print_emps() print ( isinstance (mgr_1, Manager)) print ( isinstance (mgr_1, Employee)) print ( isinstance (mgr_1, Developer)) print ( issubclass (Manager, Employee)) print ( issubclass (Developer, Employee)) print ( issubclass (Manager, Developer))

—> Eugene Kaspersky Sue.Smith@kaspersky —> Eugene Kaspersky —> Test User Sue Smith —> Test User True True False True True False

Полный код

class Employee : num_of_emps = 0 raise_amt = 1.04 def __init__ (self, first, last, pay): self.first = first self.last = last self.pay = pay self.email = first + ‘.’ + last + ‘@kaspersky.com’ Employee.num_of_emps += 1 def fullname (self): return f ‘ ‘ def apply_raise (self): self.pay = int (self.pay * self.raise_amt) @ classmethod def set_raise_amt (cls, amount): cls.raise_amt = amount @ classmethod def from_string (cls, emp_str): first, last, pay = emp_str.split( ‘-‘ ) return cls(first, last, pay) @ staticmethod def is_workday (day): if day.weekday() == 5 or day.weekday() == 6 : return False return True class Developer (Employee): raise_amt = 1.10 def __init__ (self, first, last, pay, prog_lang): super ().__init__(first, last, pay) # also possible # Employee.__init__(self, first, last, pay) self.prog_lang = prog_lang class Manager (Employee): def __init__ (self, first, last, pay, employees= None ): super ().__init__(first, last, pay) if employees is None : self.employees = [] else : self.employees = employees def add_emp (self, emp): if emp not in self.employees: self.employees.append(emp) def remove_emp (self, emp): if emp in self.employees: self.employees.remove(emp) def print_emps (self): for emp in self.employees: print ( ‘—>’ , emp.fullname()) emp_1 = Employee( ‘Andrei’ , ‘Olegovich’ , 40000 ) dev_1 = Developer( ‘Eugene’ , ‘Kaspersky’ , 50000 , ‘Python’ ) dev_2 = Developer( ‘Test’ , ‘User’ , 60000 , ‘Java’ ) print (f «emp_1 raise_amt: » ) # 1.04 print (f «dev_1 raise_amt: » ) # 1.1 print (f «dev_1 Email: » ) print (dev_1.prog_lang) # Python mgr_1 = Manager( ‘Sue’ , ‘Smith’ , 90000 , [dev_1]) mgr_1.print_emps() print (mgr_1.email) mgr_1.add_emp(dev_2) mgr_1.print_emps() print (mgr_1.fullname()) mgr_1.remove_emp(dev_1) mgr_1.print_emps() print ( isinstance (mgr_1, Manager)) print ( isinstance (mgr_1, Employee)) print ( isinstance (mgr_1, Developer)) print ( issubclass (Manager, Employee)) print ( issubclass (Developer, Employee)) print ( issubclass (Manager, Developer))

Источник

Оцените статью