7. Основы Python. Часть III - qa-guru/knowledge-base GitHub Wiki
Данный раздел разделен на несколько частей(каждый пункт - это ссылка на соответствующий раздел):
Переход по внутренним ссылкам происходит только при открытом разделе блока в котором находится ссылка.
ООП - это объектно-ориентированное программирование, которое позволяет описывать объекты и их взаимодействие между собой. ООП сосредотачивается на создании объектов, которые содержат как данные, так и код, чтобы манипулировать данными. ООП позволяет создавать объекты, которые могут взаимодействовать друг с другом, обмениваясь данными и информацией. Под объектом понимается экземпляр класса. Класс - это шаблон для создания объектов. Он определяет атрибуты и методы, которые будут у объектов этого класса. Атрибуты - это переменные, которые хранят данные. А методы - это функции, которые могут быть вызваны для выполнения действий.
Пример класса:
class Person: # это класс Person
def __init__(self, name, age): # это конструктор класса
self.name = name # это атрибут класса
self.age = age # это атрибут класса
def say_hello(self): # это метод класса, который выводит приветствие и насчитывает возраст. Наследует атрибуты класса(self.name, self.age)
print(f'Hello, my name is {self.name} and I am {self.age} years old')
В коде выше объектом называют экземпляр класса Person. Экземпляр класса создается с помощью вызова класса, как если бы это была функция. В примере выше создание объекта выглядит так:
person = Person('John', 25) # создание объекта класса Person
В примере выше person - это объект класса Person. Он содержит атрибуты name и age, которые были переданы в конструктор класса. Также у объекта есть метод say_hello, который можно вызвать, используя точку:
person.say_hello() # вызов метода say_hello у объекта person
# Output:
# Hello, my name is John and I am 25 years old
Преимущества ООП:
- Повторное использование кода - объекты могут быть использованы в разных частях программы.
- Модульность кода - объекты могут быть использованы для разделения кода на более мелкие части.
- Гибкость кода - объекты могут быть легко изменены и расширены.
- Безопасность кода - программы, написанные с использованием ООП, обычно более надежны и безопасны.
Недостатки ООП:
- Сложность - ООП может быть сложным для понимания и использования.
- Производительность - ООП может быть менее эффективным с точки зрения производительности, чем другие подходы к программированию.
- Размер - ООП может привести к созданию больших программ, которые могут быть сложными для управления.
- Абстракция
- Инкапсуляция
- Наследование
- Полиморфизм
Нажать, чтобы раскрыть
Абстракция - это процесс создания модели объекта. Который содержит только те аспекты объекта, которые важны для решения конкретной задачи. Она позволяет скрыть сложность объекта от пользователя. В Python абстракция реализуется с помощью классов. Класс - это шаблон для создания объектов. Он определяет атрибуты и методы, которые будут у объектов этого класса.
Пример абстракции:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f'Hello, my name is {self.name} and I am {self.age} years old')
person = Person('John', 25) # создание объекта класса Person
person.say_hello() # вызов метода say_hello у объекта
# Output:
# Hello, my name is John and I am 25 years old
В примере выше класс Person - это абстракция объекта человека. Он содержит атрибуты name и age, которые характеризуют человека. И метод say_hello, который позволяет человеку поздороваться. Таким образом, класс Person абстрагирует объект человека, скрывая его сложность от пользователя.
Нажать, чтобы раскрыть
Инкапсуляция - это прием программирования, который позволяет разбить сложную систему на более простые части. Она позволяет скрыть сложность системы от пользователя.
Пример инкапсуляции:
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
def get_name(self):
return self._name
def set_name(self, name):
self._name = name
def get_age(self):
return self._age
def set_age(self, age):
self._age = age
person = Person('John', 25) # создание объекта класса Person
print(person.get_name()) # вызов метода get_name у объекта person
print(person.get_age()) # вызов метода get_age у объекта person
# Output:
# John
# 25
В примере выше атрибуты name и age инкапсулированы в классе Person. Они доступны только через методы get_name, set_name, get_age и set_age. Таким образом, инкапсуляция позволяет скрыть сложность атрибутов от пользователя и предоставить удобный интерфейс для работы с ними. set_name и set_age - это методы для установки значений атрибутов name и age. get_name и get_age - это методы для получения значений атрибутов name и age. Таким образом, инкапсуляция позволяет создавать более безопасный и удобный интерфейс для работы с атрибутами объекта.
Нажать, чтобы раскрыть
Наследование - это процесс создания нового класса на основе существующего класса. Новый класс называется подклассом, а существующий класс называется супер классом. Подкласс наследует атрибуты и методы супер класса и может добавлять к ним новые атрибуты и методы.
Пример наследования:
class Student(Person):
def __init__(self, name, age, grade):
super().__init__(name, age)
self.grade = grade
def get_grade(self):
return self.grade
student = Student('John', 25, 'A') # создание объекта класса Student
print(student.get_name()) # вызов метода get_name у объекта student
print(student.get_age()) # вызов метода get_age у объекта student
print(student.get_grade()) # вызов метода get_grade у объекта student
# Output:
# John
# 25
# A
В примере выше класс Student наследует атрибуты и методы класса Person. Он добавляет к ним новый атрибут grade и метод get_grade. Таким образом, наследование позволяет создавать новые классы на основе существующих классов и повторно использовать код.
Нажать, чтобы раскрыть
Полиморфизм - это возможность объектов с одинаковым интерфейсом иметь различную реализацию. Это позволяет использовать объекты разных классов в одном и том же контексте.
Пример полиморфизма:
class Dog:
def speak(self):
return 'Woof!'
class Cat:
def speak(self):
return 'Meow!'
def get_pet_sound(pet):
return pet.speak()
dog = Dog()
cat = Cat()
print(get_pet_sound(dog)) # вызов функции get_pet_sound с объектом dog
print(get_pet_sound(cat)) # вызов функции get_pet_sound с объектом cat
# Output:
# Woof!
# Meow!
В примере выше классы Dog и Cat имеют метод speak с одинаковым интерфейсом. Однако у них различная реализация этого метода. Таким образом, полиморфизм позволяет использовать объекты разных классов в одном и том же контексте. Аргумент pet функции get_pet_sound может быть объектом любого класса, который имеет метод speak. Таким образом, полиморфизм позволяет создавать более гибкий и универсальный код.
Нажать, чтобы раскрыть
Модули и классы являются основными строительными блоками в Python. Модули - это файлы, содержащие определения функций, классов и переменных, которые можно использовать в разных частях программы. Классы - это шаблоны для создания объектов. Они определяют атрибуты и методы, которые будут у объектов этого класса.
Пример модуля:
# file: automation.py
import time
def wait(seconds):
time.sleep(seconds)
class Browser:
def __init__(self, url):
self.url = url
def open(self):
print(f'Opening {self.url} in the browser')
def close(self):
print('Closing the browser')
В примере выше, файл automation.py содержит определение функции wait и класса Browser. Эти определения могут быть использованы в других файлах с помощью импорта:
# file: main.py
import automation
automation.wait(5) # вызов функции wait из модуля automation
browser = automation.Browser('https://www.github.com') # создание объекта класса Browser из модуля automation
browser.open() # вызов метода open у объекта browser
automation.wait(5) # ожидание 5 секунд
browser.close() # вызов метода close у объекта browser
# Output:
# Opening https://www.github.com in the browser
# Closing the browser
В примере выше, файл main.py импортирует модуль automation и использует его определения. Таким образом, модули позволяют организовать код в более мелкие части и повторно использовать его в разных частях программы.
Нажать, чтобы раскрыть
self
- это ссылка на текущий объект. Она используется для доступа к атрибутам и методам объекта внутри его методов. В Python self - это обязательный параметр для всех методов объекта, который передается автоматически при вызове метода.
Пример использования self:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f'Hello, my name is {self.name} and I am {self.age} years old')
person = Person('John', 25) # создание объекта класса Person
person.say_hello() # вызов метода say_hello у объекта
# Output:
# Hello, my name is John and I am 25 years old
В примере выше self используется для доступа к атрибутам name и age объекта внутри метода say_hello. Таким образом, self позволяет объекту взаимодействовать с самим собой.
Нажать, чтобы раскрыть
__init__()
- это конструктор класса. Он вызывается при создании объекта класса и используется для инициализации его атрибутов.
Инициализация атрибутов происходит с помощью параметров, переданных в конструктор при создании объекта.
Инициализация это процесс задания начальных значений атрибутам объекта.
Пример использования init() в классе:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person('John', 25) # создание объекта класса Person
print(person.name) # доступ к атрибуту name объекта
print(person.age) # доступ к атрибуту age объекта
# Output:
# John
# 25
В примере выше конструктор класса Person принимает два параметра: name и age. Он использует их для инициализации атрибутов name и age объекта. Таким образом, init() позволяет задать начальные значения атрибутам объекта при его создании.
Нажать, чтобы раскрыть
@classmethod
- это декоратор, который используется для определения метода класса. Метод класса принимает первым параметром ссылку на класс, а не на объект. Он может быть вызван как от объекта, так и от класса.
Пример использования @classmethod:
class Person:
count = 0 # атрибут класса
def __init__(self, name, age):
self.name = name
self.age = age
Person.count += 1 # увеличение счетчика при создании объекта
@classmethod
def get_count(cls): # метод класса
return cls.count
person1 = Person
person2 = Person
print(person1.get_count()) # вызов метода get_count от класса
print(person2.get_count()) # вызов метода get_count от класса
# Output:
# 2 # значение атрибута count = 2 потому что создано 2 объекта
# 2
В примере выше метод get_count класса Person помечен декоратором @classmethod. Он принимает первым параметром ссылку на класс (cls) и возвращает значение атрибута count. Таким образом, метод класса позволяет получить доступ к атрибутам класса и использовать их внутри метода.
Еще пример использования @classmethod:
class Math:
@classmethod
def add(cls, a, b):
return a + b
print(Math.add(2, 3)) # вызов метода add от класса
math = Math()
print(math.add(2, 3)) # вызов метода add от объекта
# Output:
# 5
# 5
В примере выше метод add класса Math помечен декоратором @classmethod. Он принимает первым параметром ссылку на класс (cls) и возвращает сумму двух чисел. Таким образом, метод класса позволяет создавать функции, которые могут быть вызваны как от объекта, так и от класса.
Нажать, чтобы раскрыть
@staticmethod
- это декоратор, который используется для определения статического метода. Статический метод не принимает ссылку на объект или класс и может быть вызван как от объекта, так и от класса.
Пример использования @staticmethod:
class Person:
@staticmethod
def say_hello(name): # статический метод
print(f'Hello, my name is {name}')
Person.say_hello('John') # вызов статического метода от класса
person = Person()
person.say_hello('John') # вызов статического метода от объекта
# Output:
# Hello, my name is John
# Hello, my name is John
В примере выше метод say_hello класса Person помечен декоратором @staticmethod. Он не принимает ссылку на объект или класс и просто выводит приветствие с именем. Таким образом, статический метод позволяет создавать функции, которые могут быть вызваны как от объекта, так и от класса.
Нажать, чтобы раскрыть
@property
- это декоратор, который используется для определения свойства. Свойство позволяет управлять доступом к атрибутам объекта и выполнять дополнительные действия при их чтении и записи.
Пример использования @property:
class Person:
def __init__(self, name):
self._name = name
@property
def name(self): # свойство для чтения атрибута name
return self._name
@name.setter
def name(self, value): # свойство для записи атрибута name
self._name = value
person = Person('John') # создание объекта класса Person
print(person.name) # чтение атрибута name
person.name = 'Mike' # запись атрибута name
print(person.name) # чтение атрибута name
# Output:
# John
# Mike
В примере выше свойство name класса Person
позволяет управлять доступом к атрибуту _name
. Оно определено с помощью декораторов @property
и @name.setter
. Таким образом, свойство позволяет выполнять дополнительные действия при чтении и записи атрибута.
.setter
- это декоратор, который определяет метод для записи атрибута. Он принимает первым параметром ссылку на свойство (name
) и вторым параметром значение, которое нужно записать в атрибут. Таким образом, свойство позволяет управлять доступом к атрибутам объекта и выполнять дополнительные действия при их чтении и записи.
Нажать, чтобы раскрыть
Дата-классы - это классы, которые используются для представления данных. Они позволяют определить атрибуты и методы, которые будут у объектов этого класса. Дата-классы позволяют создавать объекты, которые содержат как данные, так и код, чтобы манипулировать данными.
Пример использования дата-классов:
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
person = Person('John', 25) # создание объекта класса Person
print(person.name) # доступ к атрибуту name объекта
print(person.age) # доступ к атрибуту age объекта
# Output:
# John
# 25
В примере выше дата-класс Person
определен с помощью декоратора @dataclass
. Он содержит два атрибута: name
и age
. Таким образом, дата-класс позволяет определить атрибуты и методы, которые будут у объектов этого класса.
Нажать, чтобы раскрыть
Enum - это перечисление, которое позволяет определить набор именованных констант. Он позволяет создавать набор значений, которые могут быть использованы в разных частях программы.
Пример использования Enum:
from enum import Enum
class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3
print(Color.RED) # доступ к значению RED
print(Color.GREEN) # доступ к значению GREEN
print(Color.BLUE) # доступ к значению BLUE
# Output:
# Color.RED
# Color.GREEN
# Color.BLUE
В примере выше Enum Color
определяет три именованных константы: RED
, GREEN
и BLUE
. Таким образом, Enum
позволяет определить набор именованных констант, где каждая константа имеет свое уникальное имя и значение.
Нажать, чтобы раскрыть
Ты увидишь полное решение задачи, если нажмешь на кнопку ниже. Ты уверен, что хочешь это сделать?
Да, я осознаю все риски, и спишу
А может подумаешь еще раз?
Уже думал, хочу списать
А может загуглишь самостоятельно?
Не помогло, дай списать
Спишешь, но не скажешь никому?
Так и думал/ла сделать
А вдруг ты сможешь сам?
Может, но не сейчас
Ладно, смотри решение, но не забудь разобраться в нем.
Обязуюсь разобраться после того как спишу
Class Product
class Product:
def check_quantity(self, quantity) -> bool:
"""
TODO Верните True если количество продукта больше или равно запрашиваемому
и False в обратном случае
"""
return self.quantity >= quantity # проверка наличия продукта, возвращает True если продукта достаточно и False если не достаточно
def buy(self, quantity):
"""
TODO реализуйте метод покупки
Проверьте количество продукта используя метод check_quantity
Если продуктов не хватает, то выбросите исключение ValueError
"""
if self.check_quantity(quantity): # проверка наличия продукта
self.quantity -= quantity # уменьшение количества продукта
else:
raise ValueError("Not enough products") # выброс исключения если продукта не хватает
Class Cart
class Cart:
# Словарь продуктов и их количество в корзине
products: dict[Product, int]
def __init__(self):
# По-умолчанию корзина пустая
self.products = {}
def add_product(self, product: Product, buy_count=1):
"""
Метод добавления продукта в корзину.
Если продукт уже есть в корзине, то увеличиваем количество
"""
if product in self.products: # проверка наличия продукта в корзине
self.products[product] += buy_count # увеличение количества продукта в корзине, если он уже есть
else:
self.products[product] = buy_count # добавление продукта в корзину
def remove_product(self, product: Product, remove_count=None):
"""
Метод удаления продукта из корзины.
Если remove_count не передан, то удаляется вся позиция
Если remove_count больше, чем количество продуктов в позиции, то удаляется вся позиция
"""
if product in self.products: # проверка наличия продукта в корзине
if remove_count is None or self.products[product] <= remove_count: # проверка наличия remove_count
del self.products[product] # удаление продукта из корзины
else:
self.products[product] -= remove_count # уменьшаем количество продукта в корзине
def clear(self):
self.products.clear() # очистка корзины
def get_total_price(self) -> float:
total = 0 # общая стоимость
for product, count in self.products.items(): # перебор продуктов в корзине
total += product.price * count # подсчет общей стоимости
return total # возвращение общей стоимости
def buy(self):
"""
Метод покупки.
Учтите, что товаров может не хватать на складе.
В этом случае нужно выбросить исключение ValueError
"""
for product, thing in self.products.items(): # перебор продуктов в корзине
if product.quantity < thing: # проверка наличия продукта на складе
raise ValueError('Not enough products') # выброс исключения если продукта не хватает
else:
product.buy(thing) # покупка продукта