Объектно-ориентированное программирование (ООП) — это способ написания кода, который позволяет представлять данные и действия над ними в виде объектов. Каждый объект представляет собой некоторую сущность, которую можно описать с помощью свойств и методов.
Основные понятия объектно-ориентированного программирования
Класс
Класс — это определение объекта, которое содержит набор атрибутов (переменных) и методов (функций). Он представляет собой тип объекта, а объекты этого класса являются экземплярами этого типа. Класс можно рассматривать как чертеж, по которому создаются объекты.
Пример аналогии из материального мира: Класс можно сравнить с чертежом здания. Он содержит все необходимые инструкции для создания объектов.
Пример реализации на Python:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def say_hello(self):
print(f"Привет, меня зовут {self.name} и мне {self.age} лет.")
Объекты
Объект — это экземпляр класса. Он содержит значения атрибутов, определенных в классе, и может выполнять методы, определенные в классе.
Пример из реального мира. По чертежу из предыдущего примера построили несколько зданий. Причем здания имели уникальные свойства, которые отличали их друг от друга.
Пример2. Класс — автомобиль. Объекты: Lada X-Ray, Mercedes-Benz CLK
Пример реализации на Python:
person = Person("Иван", 30)
person.say_hello() # Вывод: Привет, меня зовут Иван и мне 30 лет.
Атрибуты
Атрибут — это переменная, определенная в классе, которая хранит значение, относящееся к объекту. Атрибуты могут быть публичными или приватными.
Пример аналогии из материального мира: Атрибут можно сравнить с характеристикой объекта, например, цветом или размером. Например, красный автомобиль или количество углов многоугольника
Пример реализации на Python:
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
car = Car("Toyota", "Camry", 2022)
Методы
Метод — это функция, которая определена внутри класса и может быть вызвана у объектов этого класса. Методы в классах описывают действия, которые объекты могут выполнять.
Давай рассмотрим пример с классом «Животное». Представим, что у нас есть класс «Животное», атрибутами которого являются имя животного и его возраст. Методом может быть, например, метод «голос», который описывает звук, который издает животное. Этот метод может иметь доступ к атрибутам класса, таким как имя животного.
Пример кода на Python:
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def sound(self):
pass # этот метод будет определен в дочерних классах
class Cat(Animal):
def sound(self):
return "Meow"
class Dog(Animal):
def sound(self):
return "Woof"
cat = Cat("Tom", 3)
dog = Dog("Rex", 5)
print(cat.sound()) # выводит "Meow"
print(dog.sound()) # выводит "Woof"
Здесь мы определяем класс «Животное» с методом «звук», который не имеет реализации и будет переопределен в дочерних классах. Затем мы определяем дочерние классы «Кошка» и «Собака», которые наследуют класс «Животное». В каждом дочернем классе мы переопределяем метод «звук», чтобы он возвращал соответствующий звук для каждого класса. Наконец, мы создаем объекты классов «Кошка» и «Собака» и вызываем их методы «звук». В результате мы получаем разные звуки для каждого объекта в зависимости от его класса.
Конструктор и деструктор
Конструктор и деструктор являются специальными методами класса в Python, которые вызываются автоматически при создании и удалении экземпляров класса соответственно.
Конструктор класса обычно используется для инициализации экземпляра класса, установки его начальных значений. В Python, конструктором является метод с именем __init__
, который автоматически вызывается при создании экземпляра класса.
Пример конструктора:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
В данном примере, конструктор класса Person
инициализирует атрибуты name
и age
для экземпляра класса при его создании.
Деструктор класса используется для освобождения ресурсов, которые были выделены для экземпляра класса, когда он больше не нужен и удаляется из памяти. В Python, деструктором является метод с именем __del__
, который автоматически вызывается при удалении экземпляра класса.
Пример деструктора:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __del__(self):
print(f"{self.name} удален из памяти")
person = Person("Alice", 25)
del person # "Alice удален из памяти" будет напечатано в консоль
Ключевое слово self
В примерах программ, в реализации методов, вы могли наблюдать конструкции вида self
Ключевое слово self
используется для обращения к атрибутам и методам класса из его методов. Когда мы создаем экземпляр класса и вызываем его методы, Python автоматически передает этот экземпляр в метод в качестве первого аргумента (названный self
по соглашению). Это позволяет методам получать доступ к атрибутам и методам экземпляра класса.
Пример использования self
:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def introduce(self):
print(f"Привет, меня зовут {self.name} и мне {self.age} лет.")
person = Person("Alice", 25)
person.introduce() # "Привет, меня зовут Alice и мне 25 лет." будет напечатано в консоль
В данном примере, метод introduce
класса Person
использует атрибуты name
и age
, которые принадлежат конкретному экземпляру класса, доступ к которым он получает через self
.
Основные принципы ООП
Наследование
Наследование — это один из основных принципов объектно-ориентированного программирования, который позволяет создавать новый класс на основе уже существующего класса, наследуя его свойства и методы.
Для того чтобы создать новый класс, который наследует свойства и методы от уже существующего класса, необходимо указать название базового класса в определении нового класса в качестве аргумента.
Например, если класс A — это базовый класс, а класс B наследует его свойства и методы, то определение класса B будет выглядеть следующим образом:
class A:
def __init__(self, x):
self.x = x
def method_a(self):
print("Метод A")
class B(A):
def __init__(self, x, y):
super().__init__(x)
self.y = y
def method_b(self):
print("Метод B")
В этом примере класс B наследует свойства и методы от класса A, который определяет атрибут x и метод method_a. При этом, класс B определяет свой собственный атрибут y и метод method_b.
Наследование позволяет использовать уже существующий код, который был написан в базовом классе, и расширять его функциональность в новом классе. Также наследование может упрощать код и делать его более читаемым, так как код, который используется в нескольких классах, можно вынести в базовый класс.
Ключевое слово super
Ключевое слово super
используется для вызова методов из базового класса в наследующем классе. Оно позволяет обращаться к методам базового класса, даже если их имена переопределены в классе-наследнике.
Множественное наследование
Наследование в Python поддерживает множественность, то есть класс может наследовать свойства и методы нескольких базовых классов. В этом случае определение класса-наследника будет выглядеть следующим образом:
class A:
def method_a(self):
print("Метод A")
class B:
def method_b(self):
print("Метод B")
class C(A, B):
pass
Класс C наследует свойства и методы от классов A и B.
Инкапсуляция
Инкапсуляция – это принцип, который заключается в скрытии реализации от пользователя и предоставлении интерфейса для работы с объектом.
Другими словами, вместо того, чтобы напрямую обращаться к свойствам объекта, мы взаимодействуем с ним через его методы. Это позволяет упростить использование объекта и защитить его состояние от некорректного изменения.
Пример аналогии из материального мира: мы используем пульт для управления телевизором, не зная, как работает сам телевизор.
Пример реализации на Python:
class Car:
def __init__(self, make, model, year):
self.make = make
self.model = model
self.year = year
self.__odometer = 0
def get_odometer(self):
return self.__odometer
def update_odometer(self, mileage):
if mileage >= self.__odometer:
self.__odometer = mileage
else:
print("You can't roll back an odometer!")
my_car = Car('Toyota', 'Corolla', 2020)
my_car.update_odometer(5000)
print(my_car.get_odometer()) # 5000
В примере мы создали класс Car
, который имеет атрибуты make
, model
и year
, а также методы get_odometer
и update_odometer
. Однако, атрибут __odometer
был объявлен с двумя подчеркиваниями в начале, что делает его приватным, то есть скрытым от прямого доступа. Вместо этого мы создали методы get_odometer
и update_odometer
, которые позволяют получать и изменять значение этого атрибута через интерфейс объекта.
Полиморфизм
Полиморфизм — это способность объекта вести себя по-разному в зависимости от контекста использования. Это означает, что объект может быть использован в разных контекстах и при этом вести себя по-разному. В Python полиморфизм реализуется через использование одного и того же метода с различными параметрами.
Примером полиморфизма может служить функция len(), которая возвращает длину объекта в зависимости от типа объекта. Если объект является строкой, то len() возвращает количество символов в строке, если объект — списком, то длина списка и так далее.
Абстракция
Абстракция — это способ представления объектов и их поведения на более высоком уровне абстракции, чем конкретная реализация. Абстракция позволяет скрыть детали реализации и представить объекты в более удобной форме для пользователя. В Python абстракция реализуется через использование классов и интерфейсов.
Примером абстракции может служить класс Shape, который представляет абстрактную геометрическую фигуру. Этот класс может иметь различные методы, например, метод для вычисления площади фигуры или метод для вычисления периметра фигуры. Реализация методов может отличаться для каждого конкретного подкласса Shape, например, для классов Circle, Square и Triangle, но для пользователя эти подклассы выглядят как абстрактные геометрические фигуры со своими особенностями.
Создание собственных операторов
В Python есть возможность создания собственных операторов при реализации классов с помощью перегрузки методов. Перегрузка методов — это способ определения нового поведения для стандартных операторов языка (например, +, -, *, /, <, > и т.д.) для экземпляров классов.
Операторы могут быть перегружены с помощью специальных методов, которые начинаются и заканчиваются двойными подчеркиваниями (__). Например, чтобы определить поведение оператора + для экземпляров класса, можно определить метод add(). Этот метод принимает два аргумента — self (экземпляр класса) и other (другой объект, с которым производится операция сложения), и возвращает результат операции.
Например, рассмотрим класс Vector, который представляет собой вектор в трехмерном пространстве. Мы можем определить операторы сложения и вычитания для векторов следующим образом:
class Vector:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y, self.z + other.z)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y, self.z - other.z)
Теперь мы можем складывать и вычитать экземпляры класса Vector, используя стандартные операторы + и -:
v1 = Vector(1, 2, 3)
v2 = Vector(4, 5, 6)
v3 = v1 + v2
v4 = v2 - v1
print(v3.x, v3.y, v3.z) # 5 7 9
print(v4.x, v4.y, v4.z) # 3 3 3
Также можно перегрузить другие операторы, например, операторы сравнения (<, >, <=, >=, ==, !=), операторы индексации ([]) и т.д.
Важно понимать, что перегрузка стандартных операторов должна использоваться только в тех случаях, когда это имеет смысл с точки зрения логики класса. Неправильное использование перегрузки может привести к неочевидным ошибкам в коде.
Имена методов для перегрузки стандартных операторов
Также можно перегрузить другие операторы, например, операторы сравнения (<, >, <=, >=, ==, !=), операторы индексации ([]) и т.д.
Важно понимать, что перегрузка стандартных операторов должна использоваться только в тех случаях, когда это имеет смысл с точки зрения логики класса. Неправильное использование перегрузки может привести к неочевидным ошибкам в коде.
__add__(self, other)
для оператора сложения (+)__sub__(self, other)
для оператора вычитания (-)__mul__(self, other)
для оператора умножения (*)__truediv__(self, other)
для оператора деления (/)__mod__(self, other)
для оператора остатка от деления (%)__pow__(self, other)
для оператора возведения в степень (**)__lt__(self, other)
для оператора меньше (<)__le__(self, other)
для оператора меньше или равно (<=)__eq__(self, other)
для оператора равенства (==)__ne__(self, other)
для оператора неравенства (!=)__gt__(self, other)
для оператора больше (>)__ge__(self, other)
для оператора больше или равно (>=)
Эти методы должны быть определены в классе, который перегружает соответствующий оператор. Они принимают два аргумента — self
и other
, где self
представляет объект, на котором вызывается оператор, а other
представляет объект, с которым выполняется операция. Возвращаемое значение этих методов должно быть результатом выполнения соответствующей операции.
Перегрузка операторов индексации
Перегрузка оператора индексации []
позволяет создавать собственные объекты, которые можно обращать, как к элементам массива, используя квадратные скобки.
Пример реализации класса, который использует перегрузку оператора индексации:
class MyList:
def __init__(self, items):
self.items = items
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
my_list = MyList([1, 2, 3])
print(my_list[0]) # Output: 1
my_list[0] = 4
print(my_list[0]) # Output: 4
В данном примере класс MyList
имеет атрибут items
, который хранит элементы списка. Перегрузка оператора __getitem__
позволяет обращаться к элементам списка через квадратные скобки, как если бы объект MyList
был списком. Перегрузка оператора __setitem__
позволяет изменять элементы списка через оператор присваивания =
.
В данном примере my_list[0]
вернет значение 1, а затем его значение будет изменено на 4 через оператор my_list[0] = 4
. После этого my_list[0]
вернет значение 4.
Пример реализации ООП
Рассмотрим следующие классы:
import math
# Абстрактный класс Фигура, содержит абстрактные методы для наследников
class Figure:
def area(self):
pass
def perimeter(self):
pass
# Класс Круг, является наследником класса Фигура
class Circle(Figure):
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def perimeter(self):
return 2 * math.pi * self.radius
# Класс Прямоугольник, является наследником класса Фигура
class Rectangle(Figure):
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
def perimeter(self):
return 2 * (self.length + self.width)
# Класс Квадрат, является наследником класса Прямоугольник, используется полиморфизм
class Square(Rectangle):
def __init__(self, side):
super().__init__(side, side)
# Класс Треугольник, является наследником класса Фигура
class Triangle(Figure):
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def area(self):
s = (self.a + self.b + self.c) / 2
return math.sqrt(s * (s - self.a) * (s - self.b) * (s - self.c))
def perimeter(self):
return self.a + self.b + self.c
# Пример использования классов
circle = Circle(5)
print("Площадь круга: ", circle.area())
print("Периметр круга: ", circle.perimeter())
rectangle = Rectangle(3, 4)
print("Площадь прямоугольника: ", rectangle.area())
print("Периметр прямоугольника: ", rectangle.perimeter())
square = Square(5)
print("Площадь квадрата: ", square.area())
print("Периметр квадрата: ", square.perimeter())
triangle = Triangle(3, 4, 5)
print("Площадь треугольника: ", triangle.area())
print("Периметр треугольника: ", triangle.perimeter())
В данном примере использованы все основные принципы ООП:
- Наследование: классы Круг, Прямоугольник, Квадрат, Треугольник являются наследниками абстрактного класса Фигура.
- Инкапсуляция: данные, необходимые для расчетов (радиус, стороны), хранятся внутри объектов классов, и доступ к ним осуществляется через методы.
- Полиморфизм: методы area и perimeter рассчитывают площадь и периметр, исходя из особенностей фигуры
- Абстракция: класс Figure — абстрактный, его методы не работают напрямую и созданы для реализации классов-потомков.
Также мы можем использовать полиморфизм для вывода информации о фигурах:
def print_shape_info(shape):
print("Area: ", shape.area())
print("Perimeter: ", shape.perimeter())
circle = Circle( 5)
rectangle = Rectangle(4, 6)
print_shape_info(circle)
print_shape_info(rectangle)
В этом примере мы определяем функцию print_shape_info()
, которая принимает объект класса Figure
. Эта функция выводит информацию о цвете, площади и периметре фигуры. Затем мы создаем объекты классов Circle
и Rectangle
и вызываем функцию print_shape_info()
для каждого из них. Заметьте, что мы можем передавать объекты разных классов в эту функцию, потому что они оба наследуют класс Shape
и реализуют методы area()
и perimeter()
.
Также мы можем использовать перегрузку операторов для сравнения фигур:
class Circle(Shape):
#...
def __lt__(self, other):
return self.radius < other.radius
class Rectangle(Shape):
#...
def __lt__(self, other):
return self.area() < other.area()
circle1 = Circle(5)
circle2 = Circle( 3)
rectangle1 = Rectangle(4, 6)
rectangle2 = Rectangle(3, 5)
print(circle1 < circle2) # False
print(rectangle1 < rectangle2) # False
print(circle1 < rectangle1) # TypeError: '<' not supported between instances of 'Circle' and 'Rectangle'
Здесь мы определяем метод __lt__()
, который позволяет сравнивать объекты
Специальные методы в классах
Специальные методы начинаются и заканчиваются двойным подчеркиванием, например, __init__
или __str__
.
Эти методы предоставляют специальные функциональности для классов, что делает их более мощными и гибкими.
Вы можете вызвать специальные методы напрямую, но это обычно не рекомендуется.
Специальные методы, также известные как магические методы или «dunder» (double underscore) методы, обычно вызываются автоматически в определенных ситуациях интерпретатором Python, и в большинстве случаев нет необходимости вызывать их явным образом.
Например, метод __init__
вызывается автоматически при создании нового экземпляра класса, а метод __str__
вызывается при использовании функции str()
или при передаче объекта в функцию print()
.
Однако, если есть конкретная необходимость вызвать какой-то из этих методов явно, вы можете сделать это следующим образом:
class MyClass:
def __init__(self, value):
self.value = value
obj = MyClass(42)
# Вызов метода __init__ напрямую (не рекомендуется)
MyClass.__init__(obj, 10)
# Вызов метода __str__ напрямую
result = MyClass.__str__(obj)
print(result)
Однако, повторюсь, в большинстве случаев это не требуется, и лучше полагаться на автоматическое вызывание этих методов интерпретатором. Напрямую вызывать магические методы стоит только в очень редких сценариях, когда вы точно знаете, что делаете.
Примеры работы с «магическими методами».
__init__(self, ...)
: Этот метод вызывается при создании нового объекта. Здесь инициализируются атрибуты объекта.class Person: def __init__(self, name, age): self.name = name self.age = age # Использование person = Person("John", 25)
__str__(self)
: Возвращает строковое представление объекта. Часто используется для удобного вывода информации об объекте.class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): return f"{self.name}, {self.age} years old" # Использование person = Person("John", 25) print(person) # Вывод: John, 25 years old
__len__(self)
: Возвращает длину объекта. Этот метод часто используется для контейнеров, таких как списки или строки.class ShoppingCart: def __init__(self, items): self.items = items def __len__(self): return len(self.items) # Использование cart = ShoppingCart(["item1", "item2", "item3"]) print(len(cart)) # Вывод: 3
__add__(self, other)
: Определяет поведение оператора сложения для объектов.class Point: def __init__(self, x, y): self.x = x self.y = y def __add__(self, other): return Point(self.x + other.x, self.y + other.y) # Использование point1 = Point(1, 2) point2 = Point(3, 4) result = point1 + point2 print(result.x, result.y) # Вывод: 4 6
__repr__(self)
: Этот метод предоставляет «формальное» строковое представление объекта и используется функциейrepr()
. Он часто используется для отладочных целей.class Point: def __init__(self, x, y): self.x = x self.y = y def __repr__(self): return f"Point({self.x}, {self.y})" # Использование point = Point(1, 2) print(repr(point)) # Вывод: Point(1, 2)
__getitem__(self, key)
и__setitem__(self, key, value)
: Позволяют классу поддерживать доступ к элементам как к элементам последовательности (например, списку).class MyList: def __init__(self): self.items = [] def __getitem__(self, index): return self.items[index] def __setitem__(self, index, value): self.items[index] = value # Использование my_list = MyList() my_list.items = [1, 2, 3] print(my_list[1]) # Вывод: 2 my_list[1] = 42 print(my_list.items) # Вывод: [1, 42, 3]
__iter__(self)
и__next__(self)
: Позволяют классу поддерживать итерацию.__iter__
возвращает объект итератора, а__next__
возвращает следующий элемент.class Countdown: def __init__(self, start): self.start = start def __iter__(self): return self def __next__(self): if self.start <= 0: raise StopIteration else: self.start -= 1 return self.start + 1 # Использование countdown = Countdown(5) for i in countdown: print(i) # Вывод: # 5 # 4 # 3 # 2 # 1
__del__(self)
: Этот метод вызывается при удалении объекта. Однако, из-за неопределённости момента вызова и необходимости использовать сборщик мусора, его использование не рекомендуется, и вместо этого рекомендуется использовать метод__enter__
и__exit__
для управления ресурсами.class MyClass: def __del__(self): print("Этот объект уничтожен") # Использование obj = MyClass() del obj # Вывод: "Этот объект уничтожен"
__call__(self, ...)
: Позволяет вызывать экземпляр класса, как если бы он был функцией.class CallableClass: def __call__(self, x): return x * 2 # Использование obj = CallableClass() result = obj(3) # Вывод: 6
__eq__(self, other)
: Определяет поведение оператора сравнения равенства (==
).class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): return self.x == other.x and self.y == other.y # Использование point1 = Point(1, 2) point2 = Point(1, 2) print(point1 == point2) # Вывод: True
__hash__(self)
: Возвращает хэш-значение объекта, используемое, например, при добавлении объектов в множество или использовании их в качестве ключей в словаре.class HashableClass: def __init__(self, value): self.value = value def __hash__(self): return hash(self.value) # Использование obj1 = HashableClass(42) obj2 = HashableClass(42) my_set = {obj1, obj2} print(len(my_set)) # Вывод: 1
Контекстный менеджер в Python
Контекстный менеджер в Python — это объект, который реализует методы __enter__
и __exit__
, позволяя использовать его с оператором with
.
Он обеспечивает управление кодом, когда требуются операции явного освобождения памяти или очистки.
Методы __enter__
и __exit__
предоставляют способ реализации управления ресурсами и контекстного менеджера в Python. Они используются в совместном с оператором with
для обеспечения блока кода с определёнными начальными и конечными действиями. Это часто применяется для работы с ресурсами, такими как файлы или сетевые соединения, где важно обеспечить корректное освобождение ресурсов даже в случае возникновения исключений.
- Метод
__enter__(self)
: Выполняется при входе в блокwith
. Возвращает значение, которое связывается с переменной после ключевого словаas
в оператореwith
. Этот метод выполняет предварительную настройку и инициализацию. - Метод
__exit__(self, exc_type, exc_value, traceback)
: Выполняется при выходе из блокаwith
. Принимает три аргумента, которые предоставляют информацию об исключении (если оно произошло). Если в блокеwith
не произошло исключение, то аргументы будутNone
. Этот метод используется для очистки и освобождения ресурсов.
Пример использования собственного контекстного менеджера с __enter__
и __exit__
:
class MyContextManager:
def __enter__(self):
print("Входим в контекст")
# Можно выполнить предварительные настройки, инициализацию и вернуть значение
return self
def __exit__(self, exc_type, exc_value, traceback):
print("Выходим из контекста")
# Можно выполнить очистку ресурсов здесь
# Использование контекстного менеджера
with MyContextManager() as cm:
print("Внутри блока with")
# Дополнительный код внутри блока with
# После выхода из блока with
print("За пределами блока with")
В приведенном примере метод __enter__
возвращает экземпляр класса MyContextManager
. Это значение связывается с переменной cm
, которую мы можем использовать внутри блока with
. При выходе из блока with
вызывается метод __exit__
, который выполняет необходимую очистку.
Контекстные менеджеры и методы __enter__
и __exit__
часто используются при работе с файлами, блоками данных, базами данных и другими ресурсами, требующими управления жизненным циклом.
Примеры работы контекстных менеджеров
- Работа с файлами:
# Без использования контекстного менеджера file = open('example.txt', 'r') content = file.read() file.close() # С использованием контекстного менеджера with open('example.txt', 'r') as file: content = file.read()
Использование контекстного менеджера
with open(...) as ...
гарантирует закрытие файла после завершения блока кода. - Работа с сетевыми соединениями:
import socket # Без использования контекстного менеджера connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) connection.connect(('example.com', 80)) data = connection.recv(1024) connection.close() # С использованием контекстного менеджера with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as connection: connection.connect(('example.com', 80)) data = connection.recv(1024)
Здесь контекстный менеджер обеспечивает корректное закрытие сокета.
- Управление транзакциями в базе данных:
import sqlite3 # Без использования контекстного менеджера connection = sqlite3.connect('my_database.db') cursor = connection.cursor() cursor.execute('SELECT * FROM my_table') data = cursor.fetchall() connection.commit() connection.close() # С использованием контекстного менеджера with sqlite3.connect('my_database.db') as connection: cursor = connection.cursor() cursor.execute('SELECT * FROM my_table') data = cursor.fetchall() # Автоматически выполняется commit и закрытие соединения
В этом примере благодаря контекстному менеджеру
with
не нужно явно вызыватьcommit
иclose
. - Таймер выполнения кода:
import time class Timer: def __enter__(self): self.start_time = time.time() return self def __exit__(self, exc_type, exc_value, traceback): self.end_time = time.time() elapsed_time = self.end_time - self.start_time print(f"Время выполнения: {elapsed_time} секунд") # Использование таймера в контексте with Timer(): # Ваш код, для которого измеряется время выполнения time.sleep(2)
Этот пример демонстрирует, как можно использовать контекстный менеджер для измерения времени выполнения блока кода.
Защищенные и приватные методы для реализации инкапсуляции
Инкапсуляция в объектно-ориентированном программировании означает ограничение доступа к определенным методам и атрибутам класса. В Python для инкапсуляции часто используются различные конвенции и механизмы, такие как атрибуты и методы с определенными именами.
- Атрибуты и методы с префиксом одного подчеркивания
_
:- Эти атрибуты и методы считаются «защищенными», что означает, что они не должны использоваться извне класса, но доступны для наследников.
- Нет строгих правил в Python, и эта защита основана больше на соглашениях, чем на синтаксисе.
Пример:
class MyClass: def __init__(self): self._protected_attribute = 42 def _protected_method(self): return "Этот метод защищен" obj = MyClass() print(obj._protected_attribute) # Вывод: 42 print(obj._protected_method()) # Вывод: Этот метод защищен
- Атрибуты и методы с префиксом двух подчеркиваний
__
:- Эти атрибуты и методы считаются «приватными» и не должны использоваться извне класса.
- Имена приватных атрибутов и методов могут быть «манглированы» (изменены) интерпретатором Python для предотвращения конфликтов имен между классами.
Пример:
class MyClass: def __init__(self): self.__private_attribute = 42 def __private_method(self): return "Этот метод приватен" obj = MyClass() # print(obj.__private_attribute) # Ошибка: 'MyClass' object has no attribute '__private_attribute' # print(obj.__private_method()) # Ошибка: 'MyClass' object has no attribute '__private_method' print(obj._MyClass__private_attribute) # Вывод: 42 print(obj._MyClass__private_method()) # Вывод: Этот метод приватен
Используя подчеркивания в именах, вы можете указать другим программистам, какие атрибуты и методы предназначены для внутреннего использования в классе, а какие следует рассматривать как внутренние детали, доступные только изнутри класса.
Особенности:
- Инкапсуляция в Python основана на соглашениях и хороших практиках, а не на полном ограничении доступа.
- Даже приватные атрибуты могут быть доступны извне, если у пользователя есть к ним явный доступ.
- Имена приватных атрибутов манглируются для предотвращения случайного пересечения имен в подклассах.
Помните, что эти механизмы не призваны обеспечивать полную безопасность, а скорее предоставляют соглашения и указания по использованию атрибутов и методов в рамках вашего кода и библиотеки.
Геттер, сеттер и делитер. Способы работы с защищенными и приватными методами и атрибутами.
Геттер, сеттер и делитер — это термины, связанные с использованием свойств (properties) в объектно-ориентированном программировании. Они предоставляют интерфейс для доступа, установки и удаления значений атрибутов объекта. В языке программирования Python геттеры, сеттеры и делитеры могут быть определены с использованием декораторов @property
, @<attribute_name>.setter
и @<attribute_name>.deleter
.
- Геттер (Getter):
- Геттер — это метод, который используется для получения значения атрибута объекта. В Python геттеры могут быть созданы с использованием декоратора
@property
. Они позволяют обращаться к атрибуту, как если бы это был атрибут класса, а не метод.class MyClass: def __init__(self): self._my_attribute = 42 @property def my_attribute(self): return self._my_attribute obj = MyClass() print(obj.my_attribute) # Вывод: 42
- Геттер — это метод, который используется для получения значения атрибута объекта. В Python геттеры могут быть созданы с использованием декоратора
- Сеттер (Setter):
- Сеттер — это метод, который используется для установки значения атрибута объекта. Сеттеры в Python могут быть созданы с использованием декоратора
@<attribute_name>.setter
. Они вызываются при попытке установить значение атрибута.class MyClass: def __init__(self): self._my_attribute = 0 @property def my_attribute(self): return self._my_attribute @my_attribute.setter def my_attribute(self, value): if value > 0: self._my_attribute = value obj = MyClass() obj.my_attribute = 42 print(obj.my_attribute) # Вывод: 42 obj.my_attribute = -5 print(obj.my_attribute) # Вывод: 42, так как сеттер не позволит установить отрицательное значение
- Сеттер — это метод, который используется для установки значения атрибута объекта. Сеттеры в Python могут быть созданы с использованием декоратора
- Делитер (Deleter):
- Делитер — это метод, который используется для удаления значения атрибута объекта. Делитеры могут быть созданы с использованием декоратора
@<attribute_name>.deleter
.class MyClass: def __init__(self): self._my_attribute = 42 @property def my_attribute(self): return self._my_attribute @my_attribute.deleter def my_attribute(self): print(f"Deleting my_attribute with value {self._my_attribute}") del self._my_attribute obj = MyClass() del obj.my_attribute # Вывод: Deleting my_attribute with value 42 # print(obj.my_attribute) # Ошибка: 'MyClass' object has no attribute '_my_attribute'
- Делитер — это метод, который используется для удаления значения атрибута объекта. Делитеры могут быть созданы с использованием декоратора
В Python существуют различные средства поддержки геттеров и сеттеров для защищенных (protected) и приватных (private) атрибутов класса.
- Декораторы
property
,@property
,@<attribute_name>.setter
:property
— это встроенный декоратор, который позволяет создавать геттеры, сеттеры и делитеры для атрибутов класса.class MyClass: def __init__(self): self._protected_attribute = None # защищенный атрибут @property def protected_attribute(self): return self._protected_attribute @protected_attribute.setter def protected_attribute(self, value): if value > 0: self._protected_attribute = value obj = MyClass() obj.protected_attribute = 42 print(obj.protected_attribute) # Вывод: 42
- Декоратор
@property
для приватных атрибутов:- Аналогично можно использовать
property
и для приватных атрибутов.class MyClass: def __init__(self): self.__private_attribute = None # приватный атрибут @property def private_attribute(self): return self.__private_attribute @private_attribute.setter def private_attribute(self, value): if value > 0: self.__private_attribute = value obj = MyClass() obj.private_attribute = 42 print(obj.private_attribute) # Вывод: 42
- Аналогично можно использовать
- Использование декоратора
@property
безsetter
для создания read-only атрибутов:- Если вы хотите создать атрибут только для чтения (read-only), вы можете использовать только геттер.
class MyClass: def __init__(self): self.__read_only_attribute = 42 # приватный, только для чтения @property def read_only_attribute(self): return self.__read_only_attribute obj = MyClass() print(obj.read_only_attribute) # Вывод: 42 # obj.read_only_attribute = 10 # Ошибка: can't set attribute
- Если вы хотите создать атрибут только для чтения (read-only), вы можете использовать только геттер.
- Использование декоратора
@property
для создания computed атрибутов:- Вы можете использовать
property
для создания атрибутов, значения которых вычисляются на лету.class Circle: def __init__(self, radius): self._radius = radius @property def diameter(self): return 2 * self._radius circle = Circle(5) print(circle.diameter) # Вывод: 10
- Вы можете использовать
Статические методы и атрибуты
Атрибуты и методы, которые можно вызывать только напрямую из класса, а не из его экземпляров, называются статическими (static) атрибутами и методами.
Они связаны с классом, а не с конкретным экземпляром, и могут использоваться для решения задач, не требующих доступа к состоянию конкретного объекта.
Статические методы определяются с использованием декоратора @staticmethod
или ключевого слова staticmethod
. Они не требуют доступа к экземпляру и не могут использовать self
. Пример:
class MathOperations:
@staticmethod
def add(x, y):
return x + y
# Вызов статического метода напрямую из класса
result = MathOperations.add(3, 5)
print(result) # Вывод: 8
Статические атрибуты просто определяются внутри класса и используются через имя класса, а не через экземпляр. Пример:
class Configuration:
max_connections = 10
# Доступ к статическому атрибуту напрямую из класса
print(Configuration.max_connections) # Вывод: 10
Зачем нужны статические методы и атрибуты?
- Общие операции, не зависящие от конкретных экземпляров: Если у вас есть метод, который выполняет операцию, не зависящую от состояния конкретного объекта, это может быть хорошим кандидатом для статического метода. Это может сделать код более чистым и понятным.
- Совместное использование данных между экземплярами: Статические атрибуты могут использоваться для общего хранения данных между всеми экземплярами класса. Например, счетчик экземпляров:
class MyClass: instance_count = 0 def __init__(self): MyClass.instance_count += 1
Реализация абстракции через ABC
ABC, или Abstract Base Classes (абстрактные базовые классы), представляют собой механизм в Python для создания абстрактных классов. Абстрактный класс — это класс, который не предназначен для создания экземпляров, а служит в основном для определения интерфейса, который должны реализовать его подклассы. ABC в Python реализуется с использованием модуля abc
и декораторов.
Декораторы, используемые при создании абстрактных классов:
@abstractmethod
: Этот декоратор используется для указания абстрактного метода в абстрактном классе. Абстрактный метод — это метод, который должен быть реализован в подклассах. Если подкласс не реализует абстрактный метод, то он сам становится абстрактным и не может быть использован.from abc import ABC, abstractmethod class Shape(ABC): @abstractmethod def area(self): pass class Circle(Shape): def __init__(self, radius): self.radius = radius def area(self): return 3.14 * self.radius * self.radius
@abstractproperty
: Этот декоратор используется для определения абстрактного свойства в абстрактном классе. Свойства в Python представляют собой методы, которые можно вызвать без использования скобок. Свойства, помеченные@abstractproperty
, должны быть реализованы в подклассах.from abc import ABC, abstractproperty class Vehicle(ABC): @abstractproperty def speed(self): pass class Car(Vehicle): def __init__(self, speed): self._speed = speed @property def speed(self): return self._speed
@abstractclassmethod
и@abstractstaticmethod
: Подобные декораторы используются для создания абстрактных методов классов и статических методов соответственно. Они действуют аналогично@abstractmethod
, но применяются к методам класса и статическим методам.from abc import ABC, abstractclassmethod, abstractstaticmethod class DatabaseConnector(ABC): @abstractclassmethod def connect(cls, credentials): pass @abstractstaticmethod def version(): pass
Перечисляемые типы
Enum
(перечисление) в Python — это тип данных, который предоставляет способ создания и использования именованных констант. Использование Enum
может сделать ваш код более читаемым, поддерживаемым и предотвратить ошибки из-за опечаток или неправильных значений.
Вот несколько причин, почему использование Enum
может быть полезным:
- Читаемость кода:
- Именованные константы, предоставляемые
Enum
, делают код более ясным и самодокументирующимся. - Вместо использования магических чисел или строк, вы можете использовать более осмысленные имена.
from enum import Enum class Weekday(Enum): MONDAY = 1 TUESDAY = 2 WEDNESDAY = 3 THURSDAY = 4 FRIDAY = 5 SATURDAY = 6 SUNDAY = 7
- Именованные константы, предоставляемые
- Безопасность и проверка типов:
Enum
предотвращает использование неправильных значений, так как только те значения, которые указаны в перечислении, считаются допустимыми.- Это помогает избежать ошибок, связанных с опечатками и неправильными значениями.
day = Weekday.MONDAY # day = Weekday.WRONG_DAY # Ошибка: 'Weekday' has no member 'WRONG_DAY'
- Сопоставление значений:
- Вы можете сравнивать значения перечислений, что делает код более выразительным и легким для понимания.
today = Weekday.MONDAY if today == Weekday.MONDAY: print("It's Monday!")
- Вы можете сравнивать значения перечислений, что делает код более выразительным и легким для понимания.
- Итерация по значениям:
- Вы можете легко итерироваться по всем значениям в перечислении, что может быть удобным в некоторых сценариях.
for day in Weekday: print(day)
- Вы можете легко итерироваться по всем значениям в перечислении, что может быть удобным в некоторых сценариях.
- Ограничение выбора:
- Перечисления могут использоваться для явного ограничения выбора значений в определенном контексте.
def set_weekday(day): if day in Weekday: print(f"Setting the weekday to {day}") else: print("Invalid weekday") set_weekday(Weekday.MONDAY) set_weekday("InvalidDay") # Вывод: Invalid weekday
- Перечисления могут использоваться для явного ограничения выбора значений в определенном контексте.
- Читаемый вывод:
- Значениями перечисления можно обращаться, как к строкам, но они всегда будут сравниваться по их уникальным именам, что делает вывод более читаемым.
print(Weekday.MONDAY) # Вывод: Weekday.MONDAY
- Значениями перечисления можно обращаться, как к строкам, но они всегда будут сравниваться по их уникальным именам, что делает вывод более читаемым.
Классы для хранения данных. Декоратор @dataclass
В Python, декоратор dataclass
представляет собой специальный механизм для автоматического создания методов, обычно связанных с обработкой данных, в классах, предназначенных для хранения данных. Декоратор dataclass
добавляет стандартные методы, такие как __init__
, __repr__
, и другие, что делает класс более удобным для использования в сценариях, где основной целью является хранение и обработка данных.
Несколько причин, по которым можно использовать декоратор dataclass
:
- Уменьшение объема кода:
- Декоратор
dataclass
автоматически генерирует стандартные методы, такие как__init__
и__repr__
, что уменьшает объем шаблонного кода, который обычно нужно написать для создания классов для хранения данных.
- Декоратор
- Явное указание полей данных:
- Декоратор позволяет явно указать поля данных, что делает код более ясным и обеспечивает лучшую документацию для кода.
from dataclasses import dataclass @dataclass class Point: x: float y: float
- Декоратор позволяет явно указать поля данных, что делает код более ясным и обеспечивает лучшую документацию для кода.
- Неизменяемость (immutable) по умолчанию:
- По умолчанию, экземпляры классов, созданные с использованием
dataclass
, являются неизменяемыми. То есть, они не могут быть изменены после создания.from dataclasses import dataclass @dataclass(frozen=True) class Point: x: float y: float
- По умолчанию, экземпляры классов, созданные с использованием
- Поддержка сравнения:
- Декоратор
dataclass
автоматически добавляет методы для сравнения экземпляров класса, что упрощает сравнение объектов на основе их данных.from dataclasses import dataclass @dataclass class Point: x: float y: float p1 = Point(1.0, 2.0) p2 = Point(1.0, 2.0) print(p1 == p2) # Вывод: True
- Декоратор
- Поддержка хеширования:
- Если все поля данных класса являются неизменяемыми, декоратор
dataclass
также автоматически добавляет поддержку хеширования.from dataclasses import dataclass @dataclass class Point: x: float y: float p = Point(1.0, 2.0) hash_value = hash(p)
- Если все поля данных класса являются неизменяемыми, декоратор
- Поддержка десериализации (через модуль
dataclasses_json
, например):- Декоратор
dataclass
упрощает сериализацию и десериализацию объектов данных.from dataclasses import dataclass from dataclasses_json import dataclass_json @dataclass_json @dataclass class Point: x: float y: float p = Point(1.0, 2.0) # Сериализация в JSON json_data = p.to_json() # Вывод: '{"x": 1.0, "y": 2.0}' # Десериализация из JSON new_point = Point.from_json(json_data)
- Декоратор
Задания для подготовки
Задание 1. Создайте классы для представления различных геометрических тел, таких как сфера, параллелепипед, конус и т.д. Используйте принцип наследования для создания родительского класса, который будет содержать общие атрибуты и методы для всех тел, например, объем и площадь поверхности. Затем создайте дочерние классы, которые наследуют эти атрибуты и методы и добавляют дополнительные, специфичные для каждого тела.
Для решения данной задачи можно создать базовый класс GeometricSolid
со следующими общими атрибутами и методами:
volume()
— метод для вычисления объема телаsurface_area()
— метод для вычисления площади поверхности тела
Затем можно создать дочерние классы, которые будут наследовать от GeometricSolid
и добавлять свои собственные атрибуты и методы для конкретных геометрических тел.
Пример реализации:
import math
class GeometricSolid:
def volume(self):
pass
def surface_area(self):
pass
class Sphere(GeometricSolid):
def __init__(self, radius):
self.radius = radius
def volume(self):
return 4/3 * math.pi * self.radius**3
def surface_area(self):
return 4 * math.pi * self.radius**2
class Cube(GeometricSolid):
def __init__(self, length):
self.length = length
def volume(self):
return self.length**3
def surface_area(self):
return 6 * self.length**2
class Cylinder(GeometricSolid):
def __init__(self, radius, height):
self.radius = radius
self.height = height
def volume(self):
return math.pi * self.radius**2 * self.height
def surface_area(self):
return 2 * math.pi * self.radius * self.height + 2 * math.pi * self.radius**2
В этом примере мы создали три дочерних класса: Sphere
, Cube
, и Cylinder
. В каждом из них мы определили конструктор, который принимает необходимые аргументы для вычисления объема и площади поверхности. Затем мы переопределили методы volume()
и surface_area()
для каждого класса, чтобы они возвращали значения, соответствующие конкретному геометрическому телу.
Пример использования:
sphere = Sphere(5)
print("Sphere radius:", sphere.radius)
print("Sphere volume:", sphere.volume())
print("Sphere surface area:", sphere.surface_area())
cube = Cube(3)
print("Cube length:", cube.length)
print("Cube volume:", cube.volume())
print("Cube surface area:", cube.surface_area())
cylinder = Cylinder(2, 5)
print("Cylinder radius:", cylinder.radius)
print("Cylinder height:", cylinder.height)
print("Cylinder volume:", cylinder.volume())
print("Cylinder surface area:", cylinder.surface_area())
Результат выполнения:
Sphere radius: 5
Sphere volume: 523.5987755982989
Sphere surface area: 314.1592653589793
Cube length: 3
Cube volume: 27
Cube surface area: 54
Cylinder radius: 2
Cylinder height: 5
Cylinder volume: 62.83185307179586
Cylinder surface area: 62.83185307179586
Задание 2. Создайте классы для представления различных животных, например, кошек, собак, птиц и т.д. Используйте принцип наследования для создания родительского класса, который будет содержать общие атрибуты и методы для всех животных, например, возраст и цвет шерсти. Затем создайте дочерние классы, которые наследуют эти атрибуты и методы и добавляют дополнительные, специфичные для каждого вида животных, например, способность летать или область обитания.
class Animal:
def __init__(self, age, color):
self.age = age
self.color = color
def speak(self):
print("I am an animal")
class Cat(Animal):
def __init__(self, age, color, breed):
super().__init__(age, color)
self.breed = breed
def speak(self):
print("Meow")
class Dog(Animal):
def __init__(self, age, color, breed):
super().__init__(age, color)
self.breed = breed
def speak(self):
print("Woof")
class Bird(Animal):
def __init__(self, age, color, wingspan):
super().__init__(age, color)
self.wingspan = wingspan
def speak(self):
print("Chirp")
cat = Cat(2, "black", "siamese")
dog = Dog(4, "brown", "golden retriever")
bird = Bird(1, "green", 12)
print(cat.age) # Output: 2
print(dog.breed) # Output: golden retriever
print(bird.color) # Output: green
cat.speak() # Output: Meow
dog.speak() # Output: Woof
bird.speak() # Output: Chirp
В этом примере мы создали базовый класс Animal
, который содержит общие атрибуты и методы для всех животных, такие как возраст и цвет шерсти, а также метод speak
, который печатает звук, который производит животное. Затем мы создали три дочерних класса: Cat
, Dog
и Bird
, которые наследуют от Animal
и добавляют свои собственные атрибуты и методы, например, порода для кошек и собак и размах крыльев для птиц. Каждый класс также переопределяет метод speak
, чтобы выводить свой собственный звук.
В конце мы создали объекты каждого класса и вызвали их методы и атрибуты.
Задание 3. Создайте классы для представления различных транспортных средств, например, автомобилей, велосипедов, самолетов и т.д. Используйте принцип наследования для создания родительского класса, который будет содержать общие атрибуты и методы для всех транспортных средств, например, скорость и вместимость. Затем создайте дочерние классы, которые наследуют эти атрибуты и методы и добавляют дополнительные, специфичные для каждого транспортного средства, например, тип двигателя или способность летать.
class Transport:
def __init__(self, speed, capacity):
self.speed = speed
self.capacity = capacity
def move(self):
print(f"Moving at {self.speed} km/h")
def load(self, amount):
if amount <= self.capacity:
print(f"Loading {amount} passengers")
else:
print(f"Not enough capacity to load {amount} passengers")
class Car(Transport):
def __init__(self, speed, capacity, engine_type):
super().__init__(speed, capacity)
self.engine_type = engine_type
def park(self):
print("Parking the car")
def honk(self):
print("Honking the horn")
class Bicycle(Transport):
def __init__(self, speed, capacity, frame_material):
super().__init__(speed, capacity)
self.frame_material = frame_material
def ride(self):
print("Riding the bicycle")
def ring_bell(self):
print("Ringing the bell")
class Airplane(Transport):
def __init__(self, speed, capacity, engine_type, wingspan):
super().__init__(speed, capacity)
self.engine_type = engine_type
self.wingspan = wingspan
def fly(self):
print("Flying the airplane")
def land(self):
print("Landing the airplane")
Здесь класс Transport
является родительским классом для всех транспортных средств и содержит общие атрибуты и методы для них, такие как скорость и вместимость.
Затем мы создали три дочерних класса: Car
, Bicycle
и Airplane
. Класс Car
добавляет атрибут engine_type
и методы park
и honk
. Класс Bicycle
добавляет атрибут frame_material
и методы ride
и ring_bell
. Класс Airplane
добавляет атрибуты engine_type
и wingspan
и методы fly
и land
.
Теперь мы можем создавать объекты каждого класса и вызывать их методы, как показано ниже:
car = Car(100, 5, "gasoline")
car.move() # Moving at 100 km/h
car.load(3) # Loading 3 passengers
car.park() # Parking the car
car.honk() # Honking the horn
bicycle = Bicycle(20, 1, "steel")
bicycle.move() # Moving at 20 km/h
bicycle.load(1) # Loading 1 passengers
bicycle.ride() # Riding the bicycle
bicycle.ring_bell() # Ringing the bell
airplane = Airplane(800, 200, "jet", 40)
airplane.move() # Moving at 800 km/h
airplane.load(150) # Loading 150 passengers
airplane.fly() # Flying the airplane
airplane.land() # Landing the airplane
Задание 4. Создайте классы для представления различных продуктов в магазине, например, книг, музыки, электроники и т.д. Используйте принцип наследования для создания родительского класса, который будет содержать общие атрибуты и методы для всех продуктов, например, цена и описание. Затем создайте дочерние классы, которые наследуют эти атрибуты и методы и добавляют дополнительные, специфичные для каждого продукта, например, автора или продолжительность воспроизведения.
class Product:
def __init__(self, name, price, description):
self.name = name
self.price = price
self.description = description
def display_info(self):
print(f"{self.name} - {self.price} руб. ({self.description})")
class Book(Product):
def __init__(self, name, price, description, author, pages):
super().__init__(name, price, description)
self.author = author
self.pages = pages
def display_info(self):
super().display_info()
print(f"Автор: {self.author}, Количество страниц: {self.pages}")
class Music(Product):
def __init__(self, name, price, description, artist, duration):
super().__init__(name, price, description)
self.artist = artist
self.duration = duration
def display_info(self):
super().display_info()
print(f"Исполнитель: {self.artist}, Продолжительность: {self.duration}")
class Electronics(Product):
def __init__(self, name, price, description, manufacturer, power):
super().__init__(name, price, description)
self.manufacturer = manufacturer
self.power = power
def display_info(self):
super().display_info()
print(f"Производитель: {self.manufacturer}, Мощность: {self.power} Вт")
В этом примере мы создали родительский класс Product
, который содержит общие атрибуты и методы для всех продуктов. Затем мы создали дочерние классы Book
, Music
и Electronics
, которые наследуют эти атрибуты и методы, а также добавляют специфичные для каждого продукта атрибуты и методы.
Каждый дочерний класс имеет свой собственный метод display_info
, который выводит информацию о продукте, включая дополнительные атрибуты, специфичные для этого продукта.
Например, мы можем создать экземпляры классов и вызвать их методы display_info
:
book = Book("Война и мир", 1500, "Роман Л. Н. Толстого", "Лев Толстой", 1225)
book.display_info()
music = Music("Stairway to Heaven", 99, "Культовая песня группы Led Zeppelin", "Led Zeppelin", "8:02")
music.display_info()
electronics = Electronics("Samsung Galaxy S21", 69990, "Смартфон", "Samsung", 25)
electronics.display_info()
Результат
Война и мир - 1500 руб. (Роман Л. Н. Толстого)
Автор: Лев Толстой, Количество страниц: 1225
Stairway to Heaven - 99 руб. (Культовая песня группы Led Zeppelin)
Исполнитель: Led Zeppelin, Продолжительность: 8:02
Samsung Galaxy S21 - 69990 руб. (Смартфон)
Производитель: Samsung
Индивидуальное и групповое обучение «Python Junior»
Если вы хотите научиться программировать на Python, могу помочь. Запишитесь на мой курс «Python Junior» и начните свой путь в мир ИТ уже сегодня!
Контакты
Для получения дополнительной информации и записи на курсы свяжитесь со мной:
Телеграм: https://t.me/Vvkomlev
Email: victor.komlev@mail.ru
Объясняю сложное простыми словами. Даже если вы никогда не работали с ИТ и далеки от программирования, теперь у вас точно все получится! Проверено десятками примеров моих учеников.
Гибкий график обучения. Я предлагаю занятия в мини-группах и индивидуально, что позволяет каждому заниматься в удобном темпе. Вы можете совмещать обучение с работой или учебой.
Практическая направленность. 80%: практики, 20% теории. У меня множество авторских заданий, которые фокусируются на практике. Вы не просто изучаете теорию, а сразу применяете знания в реальных проектах и задачах.
Разнообразие учебных материалов: Теория представлена в виде текстовых уроков с примерами и видео, что делает обучение максимально эффективным и удобным.
Понимаю, что обучение информационным технологиям может быть сложным, особенно для новичков. Моя цель – сделать этот процесс максимально простым и увлекательным. У меня персонализированный подход к каждому ученику. Максимальный фокус внимания на ваши потребности и уровень подготовки.