Инкапсуляция и наследование в Python на примерах: код и задачи
Объектно-ориентированное программирование — это методология программирования,
основанная на представлении программы в виде совокупности объектов,
каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.
Объектно-ориентированое программирование активно оперирует следующими понятиями:
Инкапсуляция — упаковка данных и функций в единый компонент.
В общем случае, в разных языках программирования термин «инкапсуляция» относится к одному из или обоим определениям:
-
механизм языка, позволяющий ограничить доступ одних компонентов программы к другим.
Например, ограничивается доступ к переменным объекта класса.
В Python, чтобы создать в классе скрытую переменную, такую переменную, что к ней имеют доступ
только методы самого класса, нужно перед именем переменной поставить __ (два подчеркивания).
Давайте рассмотрим пример:
# coding=UTF-8
class TestClass: def __init__(self): self.public_variable = «I'm public!» self.__private_variable = «I'm too shy to be public!» def get_public_variable(self): return self.public_variable def get_private_variable(self): return self.__private_variable if __name__ == «__main__»: test_class = TestClass() print(» «.join([«Public variable:», test_class.get_public_variable()])) print(» «.join([«Public variable:», test_class.public_variable])) print(» «.join([«Private variable:», test_class.get_private_variable()])) print(» «.join([«Private variable:», test_class._private_variable]))Если вы запустите этот код, то вы получите следующее:
Public variable: I'm public!
Private variable: I'm too shy to be public!
Traceback (most recent call last): File «private_access.py», line 13, in print(» «.join([«Private variable:», test_class._private_variable]))
AttributeError: 'TestClass' object has no attribute '_private_variable'Удобство инкапсуляции в следующем:
- Безопасность: никто не может залезть внутрь класса и записать в переменные все что захочет, тем самым, сломав вашу программу;
- Удобство: рефакторинг (переписывании кода). Вы можете начать переписывать класс, переназвать переменные и вам не придется бегать по коду и менять везде test_class.public_variable на test_class.new_public_variable, вам нужно будет поменять всего одну функцию get_public_variable.
-
языковая конструкция, позволяющая связать данные с методами, предназначенными для обработки этих данных.
Эта концепция очень близка к предыдущей. Давайте посмотрим на два кода: # coding=UTF-8
class PositiveInt: __a = 0 def set_a(self, a): if a >=0: self.__a = int(a) else: print(«Wrong parameter, an internal state won't change.» ) def get_a(self): return self.__a if __name__ == «__main__»: value = PositiveInt() print(value.get_a()) value.set_a(10) print(value.get_a()) value.set_a(-10) print(value.get_a())- # coding=UTF-8
class PositiveInt: a = 0 if __name__ == «__main__»: value = PositiveInt() print(value.a) value.a = 10 print(value.a) param = -10 if param > 10: value.a = param else: print(«Wrong parameter, an internal stayte won't change.» ) print(value.a)Собственно, оба этих кода делают одно и тоже.
Давайте представим, что пришел код-ревьюер, который проверял ваш код на чистоту/читаемость/верность стиля
и сказал что нужно переименовать __a в __positive_integer, потому что так по названию переменной понятней, зачем она нужна.
То в случае кода 1 вы поменяете код в трех местах в классе PositiveInt и больше нигде.
По сути, внутренности класса поменялись, но никто из тех, кто обращался к этому классу, этого не заметил.
А в случае 2 помимо самого класса вам придется ходить по всему коду и везде менять имя переменной, что, согласитесь, не очень удобно.
А еще это может вызвать кучу ошибок.
- # coding=UTF-8
Класс — это способ описания сущности, определяющий состояние и поведение, зависящее от этого состояния,
а также правила для взаимодействия с данной сущностью (методы и уровни доступа к переменным класса).
- Для создания сложной структуры данных со сложным поведением;
- Для поддержки механизмов инкапсуляции, полиморфизма и наследования;
- Для удобства. Большая задача разбивается на много функциональных блоков меньшего размера, каждый из который реализуется классом.
Объект — это конкретный экземпляр класса, поля которого проинициализированы.
Наследование — это метод расширения функциональности классов и снижения дубликации кода, когда один класс полностью забирает себе (наследует) все поля и методы другого класса (класса родителя) и добавляет новые поля и методы или переопределяет старые, тем самым расширяя/изменяя функциональность класса в сравнении с классом-родителем.
Рассмотрим простое наследование, пусть класс Derived —> Base. В Python 3 это осуществляется следующим кодом:
class Base: pass class Derived(Base): pass
- Класс Base в данном случае является базовым классом, родительским классом, надклассом, суперклассом, предком.
- Класс Derived по отношению к нему является производным классом, дочерним классом, подклассом, потомком.
- Говорят, что Derived наследует, расширяет или специализирует Base.
- В языке Python 3 существует единый базовый класс object, который неявно является предком всех объектов вообще.
Класс Object определяет базовые методы всех классов, они могут быть переопределены у конкретного класса..
Давайте рассмотрим пример, когда это может понадобиться.
Классы создаются для объединения кода и функций, его обрабатывающих. Однако, несколько классов часто оказываются настолько похожими, что код приходится дублировать.вфаеыучюсщь
class Student(UniversityMember): group = None passToUniversity = '' status = True def checkStatus(self): return self.status def dismiss(self): self.status = False self.pass_to_university = None class Teacher(UniversityMember): cathedral = None passToUniversity = '' status = True def checkStatus(self): return self.status def dismiss(self): self.status = False self.pass_to_university = None class Administrator(UniversityMember): passToUniversity = '' status = True def checkStatus(self): return self.status def dismiss(self): self.status = False self.pass_to_university = None
В данном случае и у студента, и у преподавателя, и у администратора должны быть свойства status и pass_to_university, возможность проверки статуса и возможность увольнения.
Можно заметить, что в примере очень много дублирующегося кода. Это плохо. Если мы захотим что-то поменять, нам придется менять в трех местах как минимум.
Если забудем что-то поменять, то это приведет к ошибке. В масштабах большого программного продукта это приведет к катастрофе.
Заменим дублирование кода явным наследованием от абстактного класса (см.АДТ) UniversityMember:
class UniversityMember: passToUniversity = '' status = True def checkStatus(self): return self.status def dismiss(self): self.status = False self.pass_to_university = None class Student(UniversityMember): group = None class Teacher(UniversityMember): cathedral = None class Administrator(UniversityMember): pass
- Диаграмма, которая отображает отношения между классами называется диаграммой классов, и на ней могут быть изображены также методы и атрибуты классов.
- Язык объектно-ориентированного моделирования UML включает в себя не только диаграммы классов, но и множество других диаграмм, позволяющих лучше представить будущую программу.
- За более подробной информацией можно обратиться к Wikipedia или пойти в гугл.
- В нашем случае при помощи UML отношение классов можно представить следующим образом:
И более полная версия, включающая в себя поля и методы классов:
Любой метод можно переопределить, то есть повторно реализовать в подклассе. В этом случае для экземпляров базового класса будет вызываться базовый метод, а для экземпляров производного — перегруженный.
class Base: def hello(): print(«Hello! I'm base class!») class Derived(Base): def hello(): print(«Hello! I'm derived class!») b = Base()
d = Derived()
b.hello() # Hello! I'm base class!
d.hello() # Hello! I'm derived class!
Этот механизм называется динамическим связыванием методов или полиморфизмом.
В языке Python используется механизм грубого определения типа (утиная типизация):
#19 – Наследование, инкапсуляция, полиморфизм
Помимо классов и объектов, ООП содержит дополнительные три основные концепции: наследование, инкапсуляция и полиморфизм. За урок мы научимся прописывать и реализовывать все эти концепции в языке программирования Python.
Что такое наследование, инкапсуляция и полиморфизм мы уже рассказывали в предыдущем уроке, поэтому особого внимания сейчас на этом концентрировать не будем.
Наследование
Наследование является одним из ключевых понятий ООП. За счёт наследования можно создать один общий класс (класс родитель) и создать множество других классов (классы наследники), что будут наследовать все поля, методы и конструкторы из главного класса.
Зачем использовать наследование?
Предположим что у нас есть один большой класс «Транспорт». В классе описываются базовые характеристики для всех транспортных средств:
- поля: скорость, вес, запас хода и тому подобное;
- методы: получение информации из полей, установка новых значений;
- конструктор: пустой и по установке всех полей.
На основе класса мы спокойно можем создать объект легковой машины, объект грузовика, объект самолета и так далее. У всех объектов будут одинаковые характеристики и методы.
Мы явно понимаем, что у объекта машина и самолёт будут разные поля и характеристики. Как можно поступить:
- Можно создать два отдельных класса: «Car» и «Airplane». В каждом классе будут все методы, поля и конструкторы повторно переписанные из класса «Транспорт», а также будут новые методы, что важны только для конкретного класса;
- Можно создать два класса наследника: «Car» и «Airplane». Оба класса будут наследовать всё от класса «Транспорт» и при этом будут содержать свои дополнительные функции. Таким образом повторения кода не будет и код станет меньше и чище.
Создание классов наследников
Для создания класса наследника требуется создать класс и указать наследование от главного класса.
Пример класса наследника:
class Cars:
wheels = 4 # Общее значение для всех объектов,
# так как все машины имеют колеса
class BMW (Cars):
is_m_serias = true # Является ли модель «М» серии?
# Переменная нужна только в классе BMW
Инкапсуляция
Инкапсуляция позволяет ограничить доступ к какой-либо функции в классе. Благодаря такому подходу злоумышленники или же мы сами не сможем случайно или намерено вызвать или изменить метод.
Пример:
class Some:
def _printWords(self):
print («Попробуй меня вызвать»)
obj = Some()
obj._printWords() # Вызов функции ничего не даст
При помощи двойного подчеркивания эффект защиты усиливается, поэтому вызвать функцию будет невозможным.
Полиморфизм
Полиморфизм позволяет изменять функции в классах наследниках. Пример:class Cars:
def nothing(self): # Пустая функция
pass
class BMW (Cars):
def nothing(self, word):
print (word + «!») # Функция теперь будет работать по новому
a = BMW()
a.nothing(«Some»)
Исходный код
Весь код будет доступен после подписки на проект!
Задание к уроку
Большое задание по курсу
Также стоит посмотреть
Справочник по Python
Консультация
Необходима консультация? Вы можете задавать все свои вопросы из личного кабинета и получать ответы в удобном формате прямиком на сайте!
Тест на выбор языка
Не знаете какой язык изучать? Пройдите быстрый тест и определите какой язык программирования подходит именно вам
Пройти тест
Подписка на проект
Оформите подписку и после этого вам будут доступны: домашние задания, программный код, материалы, консультации и многое другое!
Оформить подписку
Мы используем файлы cookie, чтобы улучшить работу и повысить эффективность сайта. Продолжая пользование данным сайтом, вы соглашаетесь с использованием файлов cookie.
Объектно-ориентированное программирование в Python
19 ноября 2016 г.
Объектно-ориентированное программирование (ООП) — методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определённого класса, а классы образуют иерархию наследования.
Ключевые черты ООП:
- Инкапсуляция — это определение классов — пользовательских типов данных, объединяющих своё содержимое в единый тип и реализующих некоторые операции или методы над ним. Классы обычно являются основой модульности, инкапсуляции и абстракции данных в языках ООП.
- Наследование — способ определения нового типа, когда новый тип наследует элементы (свойства и методы) существующего, модифицируя или расширяя их. Это способствует выражению специализации и генерализации.
- Полиморфизм позволяет единообразно ссылаться на объекты различных классов (обычно внутри некоторой иерархии). Это делает классы ещё удобнее и облегчает расширение и поддержку программ, основанных на них.
Класс — универсальный, комплексный тип данных, состоящий из тематически единого набора «полей» (переменных более элементарных типов) и «методов» (функций для работы с этими полями), то есть он является моделью информационной сущности с внутренним и внешним интерфейсами для оперирования своим содержимым (значениями полей). Классы служат для объединения функционала, связанного общей идеей и смыслом, в одну сущность, у которой может быть свое внутреннее состояние, а также методы, которые позволяют модифицировать это состояние.
Объект — сущность в адресном пространстве вычислительной системы, появляющаяся при создании экземпляра класса (например, после запуска результатов компиляции и связывания исходного кода на выполнение).
Типы данных (такие как int, float и др.) в Python являются классами, структуры данных (dict, list, …) — это также классы.
Для того, что узнать, принадлежит ли объект к определённому типу (т.е. классу), существует стандартная функция isinstance.
num = 13
isinstance(num, int) ['__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__']
russia = Country() china = Country() russia.name = «Russia» china.name = «China»
Первым аргументом метод __init__ принимает ссылку на только что созданный экземпляр класса, далее могут идти другие аргументы. Внутри инициализатора мы можем по ссылке self установить так называемые атрибуты экземпляра. В данном случае мы ставим атрибут экземпляра name и присваиваем ему аргумент name — имя страны:
class Country: def __init__(self, name): self.name = name russia = Country(«Russia») class Country: def __init__(self, name): self.name = name def __str__(self): return self.name def __repr__(self): return f»Country {self.name}» countries = []
country_names = [«Russia», «Kazakhstan», «China», «Italy»] for name in country_names: country = Country(name) countries.append(country)
print(countries) [Country Russia, Country Kazakhstan, Country China, Country Italy]
Иногда нужно создать переменную, которая будет работать в контексте класса, но не будет связана с каждым конкретным экземпляром (т.е. будет относиться непосредственно к самому классу, а не к экземпляру). В этом примере count (счётчик стран) — это атрибут класса:
class Country: count = 0 def __init__(self, name, population=None): self.name = name self.population = population or [] Country.count += 1 russia = Country(«Russia»)
china = Country(«China») print(Country.count, russia.count, china.__dict__) 2 2 {'name': 'China', 'population': []}
Когда счетчик ссылок на экземпляр класса достигает нуля (мы уже говорили про сборщик мусора в Python и то, что он использует счетчик ссылок), вызывается метод __del__ экземпляра. Это также магический метод, который Python нам предоставляет возможность переопределить:
class Country: def __init__(self, name): self.name = name def __del__(self): print(f»Country {self.name} has been exterminated!») byzantium = Country(«Byzantium») Country Byzantium has been exterminated!
Методы — это функции, которые действуют в контексте экземпляра класса. Таким образом, они могут менять состояние экземпляра, обращаясь к атрибутам экземпляра или делать любую другую полезную работу.
class Human: def __init__(self, name, age=0): self.name = name self.age = age def _say(self, text): print(text) def say_name(self): self._say(f»Hello, I am {self.name}») def say_how_old(self): self._say(f»I am {self._age} years old») class Country: def __init__(self, name, population=None): self.name = name self.population = population or [] def add_human(self, human): print(f»Welcome to {self.name}, {human.name}!») self.population.append(human) russia = Country(«Russia»)
settler = Human(«Gérard Depardieu», age=69)
settler.say_name()
russia.add_human(settler) Hello, I am Gérard Depardieu
Welcome to Russia, Gérard Depardieu!
class Country: def __init__(self, name, population=None): self.name = name self.population = population or [] def __add__(self, other): print(f»Countries {self.name} and {other.name} are allies now!») return Country(f»Alliance of {self.name} and {other.name}») russia = Country(«Russia»)
belarus = Country(«Belarus»)
(russia + belarus).name Countries Russia and Belarus are allies now!
'Alliance of Russia and Belarus' class Country: def __init__(self, name=None): self.name = name class Region(Country): def __init__(self, name, status=»»): ''' Вызовем инициализатор родительского класса, используя функцию super() Вызов функции super() без параметров равносилен тому, что мы указали сам класс и передали туда объект self То же самое, что Region.__init__(self) ''' super().__init__(name) self.status = status def get_info(self): return «Название: {}. Статус: {}».format(self.name, self.status) len_obl = Region(«Ленинградская область», «область»)
print(len_obl.get_info()) Название: Ленинградская область. Статус: область
Множественное наследование через классы примеси
import json class ExportJSON: def to_json(self): return json.dumps({«name»: self.name, «status»: self.status}) class ExReg(Region, ExportJSON): pass ExReg(«Ленинградская область», «область»).to_json() '{«name»: «\u041b\u0435\u043d\u0438\u043d\u0433\u0440\u0430\u0434\u0441\u043a\u0430\u044f \u043e\u0431\u043b\u0430\u0441\u0442\u044c», «status»: «\u043e\u0431\u043b\u0430\u0441\u0442\u044c»}' print(issubclass(int, object))
print(issubclass(Region, object))
print(issubclass(Region, Country))
print(issubclass(ExReg, Country))
Также в Python существуют приватные атрибуты. Для того чтобы создать приватный атрибут, необходимо его имя записать через два символа нижнего подчёркивания. Тогда в самом классе к нему можно обращаться так же, а вот для классов-наследников этот атрибут будет уже недо- ступен.
На самом деле не так всё просто, Python просто изменяет название и оно доступно в __dict__
Попробуйте сделать задание на классы из курса Погружение в Python:
Как правило задачи про классы носят не вычислительный характер. Обычно нужно написать классы, которые отвечают определенным интерфейсам. Насколько удобны эти интерфейсы и как сильно связаны классы между собой, определит легкость их использования в будущих программах.
Предположим есть данные о разных автомобилях и спецтехнике. Данные представлены в виде таблицы с характеристиками. Обратите внимание на то, что некоторые колонки присущи только легковым автомобилям, например, кол-во пассажирских мест. В свою очередь только у грузовых автомобилей есть длина, ширина и высота кузова.
Тип (car_type) | Марка (brand) | Кол-во пассажирских мест (passenger_seats_count) | Фото (photo_file_name) | Кузов ДxШxВ, м (body_whl) | Грузоподъемность, Тонн (carrying) | Дополнительно (extra) |
car | Nissan xTtrail | 4 | f1.jpeg | 2.5 | ||
truck | Man | f2.jpeg | 8x3x2.5 | 20 | ||
car | Mazda 6 | 4 | f3.jpeg | 2.5 | ||
spec_machine | Hitachi | f4.jpeg | 1.2 | Легкая техника для уборки снега |
Вам необходимо создать свою иерархию классов для данных, которые описаны в таблице.
- BaseCar
- Car(BaseCar)
- Truck(BaseCar)
- SpecMachine(BaseCar)
У любого объекта есть обязательный атрибут car_type. Он означает тип объекта и может принимать одно из значений: car, truck, spec_machine.
Также у любого объекта из иерархии есть фото в виде имени файла — обязательный атрибут photo_file_name.
В базовом классе нужно реализовать метод get_photo_file_ext для получения расширения файла (“.png”, “.jpeg” и т.д.) с фото. Расширение файла можно получить при помощи os.path.splitext.
Для грузового автомобиля необходимо разделить характеристики кузова на отдельные составляющие body_length, body_width, body_height. Разделитель — латинская буква x. Характеристики кузова могут быть заданы в виде пустой строки, в таком случае все составляющие равны 0. Обратите внимание на то, что характеристики кузова должны быть вещественными числами.
Также для класса грузового автомобиля необходимо реализовать метод get_body_volume, возвращающий объем кузова в метрах кубических.
Все обязательные атрибуты для объектов Car, Truck и SpecMachine перечислены в таблице ниже, где 1 — означает, что атрибут обязателен для объекта, 0 — атрибут должен отсутствовать.
Car | Truck | SpecMachine | |
car_type | 1 | 1 | 1 |
photo_file_name | 1 | 1 | 1 |
brand | 1 | 1 | 1 |
carrying | 1 | 1 | 1 |
passenger_seats_count | 1 | 0 | 0 |
body_width | 0 | 1 | 0 |
body_height | 0 | 1 | 0 |
body_length | 0 | 1 | 0 |
extra | 0 | 0 | 1 |
Далее необходимо реализовать функцию, на вход которой подается имя файла в формате csv. Файл содержит данные аналогичные строкам из таблицы. Вам необходимо прочитать этот файл построчно при помощи модуля стандартной библиотеки csv. Затем проанализировать строки и создать список нужных объектов с автомобилями и специальной техникой. Функция должна возвращать список объектов.
Не важно как вы назовете свои классы, главное чтобы их атрибуты имели имена:
- car_type
- brand
- passenger_seats_count
- photo_file_name
- body_width
- body_height
- body_length
- carrying
- extra
И методы:
- get_photo_file_ext
- get_body_volume
У каждого объекта из иерархии должен быть свой набор атрибутов и методов. У класса легковой автомобиль не должно быть метода get_body_volume в отличие от класса грузового автомобиля.
Функция, которая парсит строки входного массива, должна называться get_car_list.
Также обратите внимание, что все значения в csv файле при чтении будут python-строками. Нужно преобразовать строку в int для passenger_seats_count, во float для carrying, а также во float для body_width body_height, body_length.
Также ваша программа должна быть готова к тому, что в некоторых строках данные могут быть заполнены некорректно. Например, число колонок меньше . В таком случае нужно проигнорировать подобные строки и не создавать объекты. Строки с пустым значением для body_whl игнорироваться не должны. Вы можете использовать механизм исключений для обработки ошибок.
Ниже приведен пример с заготовкой кода для выполнения задания.
class CarBase: def __init__(self, brand, photo_file_name, carrying): pass class Car(CarBase): def __init__(self, brand, photo_file_name, carrying, passenger_seats_count): pass class Truck(CarBase): def __init__(self, brand, photo_file_name, carrying, body_whl): pass class SpecMachine(CarBase): def __init__(self, brand, photo_file_name, carrying, extra): pass def get_car_list(csv_filename): car_list = [] return car_list
Вам необходимо расширить функционал исходных классов, дополнить методы нужным кодом и реализовать функцию get_car_list.
Введение в классы Python — наследование, инкапсуляция и полиморфизм
Автор оригинала: David Yeoman.
Эта статья продолжается от введения классов – часть, где мы объяснили, какие классы, их компоненты, и почему мы их используем. Мы также смотрели на некоторые уникальные характеристики классов, которые помогают нам в создании чистого кода. Если вы еще не читали часть одного и нового в классы, я предложил прочитать это введение первым.
*** Учебное пособие Часть 1: Введение в классы Python ***
В современной статье мы продолжим с предыдущим примером продуктового магазина, где мы создали Сток класс. С таким классом мы рассмотрим тему наследования, что это значит и почему мы его использовали. Мы также объясним два других вопроса, специфичными для классов, будучи инкапсуляции и полиморфизмом.
Наследование – Что это и зачем использовать это?
Наследование между классами позволяет создавать новый класс, наследует все атрибуты и методы существующего класса при добавлении отдельных атрибутов и методов в новый класс.
Мы используем аналогию Родитель и Ребенок Отказ Родитель Класс – это тот, который дает наследство, а Ребенок Класс – это тот, который получает наследство. Как в жизни, так в Python.
В Часть Одна из этой статьи Мы использовали класс Сток Что позволило нам создавать продуктовые продукты с несколькими атрибутами общего на всех продуктах для продуктов, а именно таблицы, описание, цену покупки и разметку. Также были также два метода: расчет цены продажи и расчета дисконтированной цены при прохождении дисконтной фигуры. Тем не менее, разные продуктовые продукты имеют разные характеристики.
- Мы измеряем может содержание по объему в миллилитрах или мл;
- Мы взвешиваем мясо в килограммах или кг.
- Могут иметь длительный срок годности; Мясо имеет краткое использование по дате.
- Мы могли бы попытаться добавить все возможные вариации товара в классе, но это несколько громоздко.
- Как насчет того, чтобы держать эти общие предметы, обладающие всеми продуктами в классе Снабжать Как родительский класс, и создайте детские классы для мяса и банки соответственно, которые удовлетворяют конкретным потребностям этих предметов?
Вот код.
class Stock:
category = 'Groceries'
def __init__(self, stock_code, description, buy_price, mark_up):
self.code = stock_code
self.desc = description
self.buy = buy_price
self.margin = mark_up
def sell_price(self):
print('Retail price = $', round(self.buy * self.margin, 2))
def sale(self, discount):
print('The discounted price of {} is $'.format(C298.desc),
round(self.buy * self.margin * (1- discount), 2))
class Canned(Stock):
category = 'Cans'
def __init__(self, stock_code, description, buy_price, mark_up, volume, manuf):
Stock.__init__(self, stock_code, description, buy_price, mark_up)
self.volume = volume
self.manuf = manuf
def multi_buy(self):
print('Buy two {} of {} {} {} and get one free. Pay only ${}'.format(self.category, self.manuf, self.volume, self.desc, round(self.buy * self.margin, 2)))
C298 = Canned('C298', 'Chicken Soup', 0.75, 1.553, '400 mls', 'Campbells')
C298.sale(.15)
C298.multi_buy()
Давайте пройдемся через это. Код класса акций такой же, как и в предыдущей статье. Дополнение из «Классных консервированных (акций):« линия кода. Мы создали новый класс, называемый консервированным, используя тот же синтаксис, поскольку мы сделали с накладки; Тем не менее, мы назвали запас как родитель, указанные, включая его в скобки.
class Canned(Stock):
category = 'Cans'
На следующей строке мы создали категорию классов «Банки» Тогда мы использовали __init__ Функция как до того, чтобы определить параметры. Большинство параметров такие же, как в Сток класс, но мы добавили еще два, «Том» и «Мануф» Отказ Это параметры, специфичные для консервированного класса.
Следующая строка использует Запас .__ init__ ссылаться на Родитель классы параметров. Эта линия – это то, где волшебство происходит с наследством.
Позвонив классу Консервированные (Сток) И вставьте эту строку, теперь у вас есть связь между двумя классами, что позволяет передавать атрибуты и методы.
def __init__(self, stock_code, description, buy_price, mark_up, volume, manuf):
Stock.__init__(self, stock_code, description, buy_price, mark_up)
Мы передаем новые параметры «Том» и «Мануф» к Self.Volume. и Self.manuf атрибуты, то мы создали новый метод для Консервированный класс. Этот новый метод называется multi_buy () И при активации печатает этикетку, позволяющую покупателям покупать две банки продукта по цене одного.
self.volume = volume
self.manuf = manuf
def multi_buy(self):
print('Buy two {} of {} {} {} and get one free. Pay only ${}'.format(self.category, self.manuf, self.volume, self.desc, round(self.buy * self.margin, 2)))
Следующая строка кода создает или «Инстанции» объект из класса Консервированный Использование фондового кода C298 для создания банки куриного супа путем передачи параметров в требуемом порядке.
C298 = Canned('C298', 'Chicken Soup', 0.75, 1.553, '400 mls', 'Campbells')
C298.sale(.15)
C298.multi_buy()
На следующей строке мы называем метод Продажа () Для нашего объекта и пройти скидка 15%.
Обратите внимание, что Продажа () Метод относится к Запас () Класс, а не Консервированный Класс, но это доступно из-за потока наследования между Ребенок и Родитель Отказ Затем мы называем новый метод, который мы определили в Консервированный Класс называется multi_buy () Отказ Вот результат, когда мы запустим код.
# Result
The discounted price of Chicken Soup is $ 0.99
Buy two Cans of Campbells 400 mls Chicken Soup and get one free. Pay only $1.16
Как видите, у нас есть возможность использовать Продажа () Метод от родительского класса Запас () или multi_buy () Метод от детского класса, Консервированный Отказ Здесь лежит часть магии наследования.
Мы можем создать столько детских классов от Сток Как мы желаем. Давайте создадим класс для мяса.
Как мы уже говорили, мы измеряем мясо по весу и необходимость устанавливают использование по дате, как это особенно скоропортящиеся продукты питания.
class Meat(Stock):
category = 'Meat'
def __init__(self, stock_code, description, buy_price, mark_up, weight, use_by):
self.kilo = weight
self.expiry = use_by
Stock.__init__(self, stock_code, description, buy_price, mark_up)
def Label(self):
print(self.desc, '
Weight: ', self.kilo, 'kgs', '
Expiry: ', self.expiry)
self.sell_price()
def Expiring(self, discount):
print('Price reduced for quick sale: ${}'.format(round(self.buy * self.margin * (1 — discount), 2)))
Этот код следует за всеми шагами, которые мы проходили на Консервированный класс. Мы создали класс Мясо (сток) , что означает, что это ребенок Сток класс.
Мы дали это категорию Мясо Затем использовал __init__ Функция для определения параметров, которые мы требуем. Два новых, которые отличаются от Сток класс ” Вес ‘и’ использовать в ‘. Затем мы передаем эти параметры для Self.kilo и self.expiry атрибуты.
Наконец, мы используем Запас .__ init__ Команда для создания ссылки на родительские параметры.
В Мясо () Мы определили два метода, специфичными для Мясо () класс. Первый – это метод для печати этикетки, который мы можем разместить снаружи упаковки мяса. Второе – это метод дисконтирования, который уменьшит мясо в цене, так как он приближается к дате истечения срока действия; Нам просто нужно пройти скидку на метод.
Теперь мы создадим или мститель, объект из Мясо () Класс для какого-то кирно стейка мы хотим продать в нашем магазине, и мы назовем два новых метода, Этикетка () и Срок действия () Отказ Тогда мы также позвоним multi_buy () Метод для куриного супа, чтобы доказать, что два объекта стейка в килки и куриный суп, созданные как детские классы родительского класса Запас () , может с радостью сосуществовать.
C401 = Meat('C401', 'Sirloin Steak', 4.16, 1.654, .324, '15 June 2021')
C298 = Canned('C298', 'Chicken Soup', 0.75, 1.553, '400 mls', 'Campbells')
C401.Label()
print()
C401.Expiring(.35)
print()
C298.multi_buy()
# Result
Sirloin Steak
Weight: 0.324 kgs
Expiry: 15 June 2021
Retail price = $ 6.88
Price reduced for quick sale: $4.47
Buy two Cans of Campbells 400 mls Chicken Soup and get one free. Pay only $1.16
Этот пример иллюстрирует, что мы можем создать множество детей родительского класса, дайте каждому свои собственные атрибуты и методы, а также доступ к способам и атрибутам в родительском языке. Теперь давайте посмотрим, как работает инкапсуляция в классах.
Инкапсуляция
Инкапсуляция – это способность, в Объектно-ориентированное-программирование , чтобы ограничить модификацию переменных, атрибутов или методов в классе.
Мы будем использовать начальную Сток класс в качестве примера, чтобы продемонстрировать это. Давайте предположим, что мы не хотим разрешить « » Self.Margin «Атрибут быть легко измененным.
Мы можем сделать это, используя один или двойной подчеркивание перед именем атрибута.
В следующем коде мы сначала покажем, насколько легко изменить атрибут.
class Stock:
category = 'Groceries'
def __init__(self, stock_code, description, buy_price, mark_up):
self.code = stock_code
self.desc = description
self.buy = buy_price
self.margin = mark_up
def sell_price(self):
print('Retail price = $', round(self.buy * self.margin, 2))
def sale(self, discount):
print('The discounted price of {} is $'.format(C298.desc),
round(self.buy * self.margin * (1 — discount), 2))
C298 = Stock('C298', 'Chicken Soup', 0.75, 1.553)
C298.sell_price()
C298.margin = 1.2
C298.sell_price()
# Result
Retail price = $ 1.16
Retail price = $ 0.9
Поэтому, позвонив атрибуту по марне и применение пересмотренного рисунка, мы можем легко изменить Mark_up применяется к нашим предметам. Теперь мы изменим код с двойными подчерками перед атрибутом и попробуйте снова изменять его.
class Stock:
category = 'Groceries'
def __init__(self, stock_code, description, buy_price, mark_up):
self.code = stock_code
self.desc = description
self.buy = buy_price
self.__margin = mark_up
def sell_price(self):
print('Retail price = $', round(self.buy * self.__margin, 2))
def sale(self, discount):
print('The discounted price of {} is $'.format(C298.desc),
round(self.buy * self.__margin * (1 — discount), 2))
C298 = Stock('C298', 'Chicken Soup', 0.75, 1.553)
C298.sell_price()
C298.margin = 1.2
C298.sell_price()
# Result
Retail price = $ 1.16
Retail price = $ 1.16
Таким образом, с добавлением двойных подведений перед атрибутом маржина, мы теперь не можем легко изменить оригинальную цифру. Для этого требуется дискретный метод, который сделает изменение при необходимости.
class Stock:
category = 'Groceries'
def __init__(self, stock_code, description, buy_price, mark_up):
self.code = stock_code
self.desc = description
self.buy = buy_price
self.__margin = mark_up
def sell_price(self):
print('Retail price = $', round(self.buy * self.__margin, 2))
def sale(self, discount):
print('The discounted price of {} is $'.format(C298.desc),
round(self.buy * self.__margin * (1 — discount), 2))
def setMargin(self, new_margin):
self.__margin = new_margin
C298 = Stock('C298', 'Chicken Soup', 0.75, 1.553)
C298.sell_price()
C298.margin = 1.2
C298.sell_price()
C298.setMargin(1.426)
C298.sell_price()
# Result
Retail price = $ 1.16
Retail price = $ 1.16
Retail price = $ 1.07
С новым SetMargin () Способ, мы теперь создали дискретные средства, с помощью которых мы можем изменить нашу маржу продаж. В вышеуказанном выше коде мы использовали новый способ изменить запас от 1,553 до 1,426, что привело к снижению цены на продажу в размере 1,07 долл. США.
Полиморфизм
Полиморфизм относится к чему-то, имеющую многие формы. В объектно-ориентированном программировании он относится к использованию той же функции для разных типов. В классах это означает, что функция равнодушна к типу класса; До тех пор, пока методы существуют, это будет использовать его.
Мы создадим подобное Этикетка () Метод в нашем Консервированный класс, который мы использовали в Мясо класс, чтобы показать это в действии. Выход каждого метода будет другим, но название метода будет одинаковым.
Тогда мы создадим функцию, которая позвонит методу Этикетка () Использование фондовых кодов у нас для мяса и Суп Отказ Как вы увидите, полиморфизм позволит обе функции работать независимо от Печать из правильных меток.
class Stock:
category = 'Groceries'
…. # Code truncated for brevity
class Canned(Stock):
category = 'Cans'
def __init__(self, stock_code, description, buy_price, mark_up, volume, manuf):
self.volume = volume
self.manuf = manuf
Stock.__init__(self, stock_code, description, buy_price, mark_up)
def Label(self):
print(self.desc, '
Volume: ', self.volume)
self.sell_price()
C298 = Canned('C298', 'Chicken Soup', 0.75, 1.553, '400 mls', 'Campbells')
class Meat(Stock):
category = 'Meat'
def __init__(self, stock_code, description, buy_price, mark_up, weight, use_by):
self.kilo = weight
self.expiry = use_by
Stock.__init__(self, stock_code, description, buy_price, mark_up)
def Label(self):
print(self.desc, '
Weight: ', self.kilo, 'kgs', '
Expiry: ', self.expiry)
self.sell_price()
C401 = Meat('C401', 'Sirloin Steak', 4.16, 1.654, .324, '15 June 2021')
def label_print(*args):
for elem in args:
elem.Label()
print()
label_print(C401, C298)
# Result
Sirloin Steak
Weight: 0.324 kgs
Expiry: 15 June 2021
Retail price = $ 6.88
Chicken Soup
Volume: 400 mls
Retail price = $ 1.16
Как вы можете видеть в предыдущем коде, Def Metal (Self): Часть метода идентична в каждом классе, но данные, которые будут напечатаны на этикетку, отличаются.
Затем мы создали функцию вне всех трех классов, называемых label_print () , и нам позволили пройти несколько аргументов к нему, используя * args синтаксис в скобках.
Тогда мы просто повторяем каждый аргумент, однако многие могут быть, и позвонить в Этикетка () Метод в применимом классе, к которому принадлежит этот аргумент.
Результат мы напечатано Этикетки для каждого объекта, созданного из двух разных детских классов.
Резюме
В этой второй статье на занятиях мы обсудили тему Наследование Показываю, как мы можем создавать классы, которые имеют родительские отношения, позволяющие использовать атрибуты и методы от родительского класса для потока к ребенку.
Затем мы обсуждали Инкапсуляция , который ограничивает изменения в переменные, атрибуты или методы в классе, используя синтаксис подчеркивания или двойного подчеркивания. Мы показали, как использование дискретного метода может сделать желаемое изменение прямо.
- Наконец, мы обсуждали полиморфизм , который в нашем случае использует одну функцию для действия по разным методам, предполагая, что метод находится в классе.
- Вы можете прочитать часть одной из этой статьи здесь:
- *** Учебное пособие Часть 1: Введение в классы Python ***
Я доверяю, эти две статьи были полезны для понимания классов в Python. Спасибо за чтение.
Оригинал: “https://blog.finxter.com/an-introduction-to-python-classes-inheritance-encapsulation-and-polymorphism/”