Объявление классов в объектно-ориентированном программировании Python
Определение классов в Python
Основной и наиболее распространённый способ создания класса в Python - использование ключевого слова class, метода __init__ для инициализации атрибутов экземпляра и явного указания self в каждом методе экземпляра. Этот подход подходит для любых задач, где требуется инкапсулировать данные и поведение.
Как создать класс с конструктором и методами?
Пример простого класса Person с именем и возрастом:
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def greet(self):
return f"Привет, меня зовут {self.name}"атрибуты класса python (атрибуты классов и объектов в python)
Метод __init__ вызывается при создании экземпляра и принимает аргументы, которые присваиваются атрибутам. Параметр self ссылается на конкретный объект. Любой метод экземпляра первым аргументом получает self.
Использование:
p = Person("Анна", 25)
print(p.greet()) # Привет, меня зовут Аннабиблиотека классов python (библиотека классов в python)
Типичные ошибки: забыть указать self в определении метода (вызовет TypeError при попытке вызвать метод), или неправильно передать аргументы в конструктор. Также часто путают атрибуты экземпляра и класса: если написать name = "" внутри тела класса, это атрибут класса, а не экземпляра.
Как автоматически создавать атрибуты с помощью dataclass?
Модуль dataclasses (Python 3.7+) упрощает объявление классов, которые в основном хранят данные. Декоратор @dataclass автоматически генерирует __init__, __repr__, __eq__ и другие методы.
Цель: сократить шаблонный код при работе с простыми контейнерами данных. Случай использования: модель данных, конфигурация, DTO (Data Transfer Object).
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
p1 = Point(1.5, 2.0)
p2 = Point(1.5, 2.0)
print(p1) # Point(x=1.5, y=2.0)
print(p1 == p2) # True
метод объекта python (методы объектов в python)
Поля можно снабжать значениями по умолчанию, валидировать с помощью __post_init__. Недостаток: классы dataclass по умолчанию изменяемы; для неизменяемых объектов нужно указать frozen=True.
Проблема: если в поле используется изменяемое значение по умолчанию (например, пустой список), все экземпляры разделяют один список. Решение: использовать field(default_factory=list).
Как добавить свойства (property) для контролируемого доступа к атрибутам?
Декоратор @property позволяет определить метод, который будет вызываться при обращении к атрибуту, как если бы это был обычный атрибут. Это используется для валидации, вычисляемых значений или создания read-only интерфейса.
Пример класса Temperature:
class Temperature:
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Температура ниже абсолютного нуля")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
t = Temperature(25)
print(t.fahrenheit) # 77.0
t.celsius = 30
print(t.fahrenheit) # 86.0метод call python (метод __call__ в python)
Случай использования: когда необходимо добавить логику чтения/записи без изменения публичного интерфейса (принцип единого доступа).
Ошибка: забыть указать @property над геттером, тогда вызов obj.celsius вернёт объект метода, а не значение. Также нельзя использовать property как декоратор для сеттера без геттера.
Как реализовать наследование и переопределение методов?
Наследование позволяет создать класс на основе другого, унаследовав его атрибуты и методы. Ключевое слово super() используется для вызова методов родительского класса.
class Animal:
def __init__(self, name):
self.name = name
def speak(self):
return "..."
class Dog(Animal):
def speak(self):
return "Гав!"
class Cat(Animal):
def __init__(self, name, color):
super().__init__(name)
self.color = color
def speak(self):
return "Мяу!"
dog = Dog("Барсик")
cat = Cat("Мурка", "серый")
print(dog.speak()) # Гав!
print(cat.speak()) # Мяу!
print(cat.name) # МуркаНаследование полезно для создания иерархий классов, избегания дублирования кода и реализации полиморфизма.
Проблемы: множественное наследование может вызвать конфликт имён (ромбовидное наследование). Для разрешения используется MRO (Method Resolution Order). Часто вместо множественного наследования применяют композицию или миксины.
Расширенные примеры определения классов
Класс-метод и статический метод
Декоратор @classmethod создаёт метод, который получает первым аргументом сам класс (cls), а не экземпляр. Используется для фабричных методов.
class Book:
def __init__(self, title, pages):
self.title = title
self.pages = pages
@classmethod
def create_from_dict(cls, data):
return cls(data['title'], data['pages'])
@staticmethod
def is_valid_title(title):
return len(title) > 0 and title[0].isupper()
book = Book.create_from_dict({'title': '1984', 'pages': 328})
print(book.title) # 1984
print(Book.is_valid_title('1984')) # True1984 True
Статический метод (@staticmethod) не получает ни self, ни cls; он ведёт себя как обычная функция, находящаяся в пространстве имён класса.
Перегрузка операторов и магические методы
Методы __add__, __str__, __repr__ и другие позволяют переопределить поведение операторов.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __repr__(self):
return f"Vector({self.x}, {self.y})"
def __eq__(self, other):
return self.x == other.x and self.y == other.y
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector(4, 6)
print(v1 == Vector(1,2)) # TrueVector(4, 6) True
Также можно реализовать __call__ для возможности вызывать объект как функцию, __iter__ и __next__ для итераторов.
Контекстный менеджер через __enter__ и __exit__
class ManagedFile:
def __init__(self, filename):
self.filename = filename
def __enter__(self):
self.file = open(self.filename, 'w')
return self.file
def __exit__(self, exc_type, exc_val, exc_tb):
if self.file:
self.file.close()
with ManagedFile('test.txt') as f:
f.write('Hello, world!')
# файл автоматически закрытСлучаи использования: работа с файлами, сетевыми подключениями, блокировками.
Дескрипторы (descriptors)
Дескрипторы реализуют протокол с методами __get__, __set__, __delete__ и позволяют управлять доступом к атрибутам на уровне класса.
class PositiveNumber:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, obj, objtype=None):
return obj.__dict__.get(self.name)
def __set__(self, obj, value):
if value <= 0:
raise ValueError("Значение должно быть положительным")
obj.__dict__[self.name] = value
class Order:
quantity = PositiveNumber()
price = PositiveNumber()
def __init__(self, quantity, price):
self.quantity = quantity
self.price = price
order = Order(10, 150.0)
print(order.quantity) # 10
# order.quantity = -5 # ValueError10
Абстрактные классы (ABC)
Модуль abc позволяет создавать классы, которые нельзя инстанцировать, и заставлять подклассы реализовывать определённые методы.
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.14159 * self.radius ** 2
# shape = Shape() # TypeError: Can't instantiate abstract class
circle = Circle(5)
print(circle.area()) # 78.5397578.53975
Абстрактные классы задают интерфейсы и гарантируют, что все наследники предоставят необходимую реализацию.
Слоты (__slots__) для экономии памяти
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
# p.z = 3 # AttributeError: 'Point' object has no attribute 'z'Слоты фиксируют набор допустимых атрибутов, уменьшают потребление памяти и ускоряют доступ к атрибутам. Используются при создании тысяч объектов, например, в играх или научных вычислениях.