from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: float
y: float
def move(self, dx: float, dy: float) -> 'Point':
return Point(x=self.x + dx, y=self.y + dy)
`frozen=True` делает класс неизменяемым. `move` возвращает новый `Point` вместо изменения существующего.
Для создания классов в Python, поддерживающих функциональный стиль программирования, можно использовать несколько подходов. Основные цели здесь – сделать объекты неизменяемыми (или максимально приближенными к ним), предоставить методы, работающие с данными объектов без их изменения, и обеспечить возможность цепочек вызовов методов для трансформации данных.
Вот несколько способов реализации:
from dataclasses import dataclass
@dataclass(frozen=True)
class ImmutablePoint:
x: int
y: int
def move(self, dx: int, dy: int) -> 'ImmutablePoint':
return ImmutablePoint(x=self.x + dx, y=self.y + dy)
Здесь `move` не изменяет исходный объект `ImmutablePoint`, а возвращает новый, с измененными координатами.
class ImmutablePoint:
__slots__ = ('_x', '_y')
def __init__(self, x: int, y: int):
self._x = x
self._y = y
@property
def x(self) -> int:
return self._x
@property
def y(self) -> int:
return self._y
def move(self, dx: int, dy: int) -> 'ImmutablePoint':
return ImmutablePoint(x=self.x + dx, y=self.y + dy)
def __repr__(self):
return f"ImmutablePoint(x={self.x}, y={self.y})"
В этом примере атрибуты `x` и `y` доступны только для чтения (через `property`), и любые операции, которые должны изменить объект, возвращают новый объект. `__slots__` предотвращает динамическое добавление новых атрибутов, повышая предсказуемость.
from toolz import pipe
@dataclass(frozen=True)
class DataProcessor:
data: list
def filter_even(self) -> 'DataProcessor':
return DataProcessor(data=list(filter(lambda x: x % 2 == 0, self.data)))
def square_values(self) -> 'DataProcessor':
return DataProcessor(data=list(map(lambda x: x * x, self.data)))
processor = DataProcessor(data=[1, 2, 3, 4, 5])
# Использование pipe для цепочки операций
result = pipe(processor,
lambda p: p.filter_even(),
lambda p: p.square_values())
print(result) # DataProcessor(data=[4, 16])
`toolz.pipe` позволяет создавать цепочки преобразований данных, делая код более читаемым.
import functools
@dataclass(frozen=True)
class Calculator:
value: int
def add(self, x: int) -> 'Calculator':
return Calculator(value=self.value + x)
increment_by_five = functools.partial(Calculator(value=0).add, 5)
result = increment_by_five() # Calculator(value=5)
Ключевые принципы:
Используя эти подходы, можно создавать классы на Python, которые в значительной степени поддерживают принципы функционального программирования, повышая предсказуемость, упрощая тестирование и делая код более удобным для сопровождения.