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, которые в значительной степени поддерживают принципы функционального программирования, повышая предсказуемость, упрощая тестирование и делая код более удобным для сопровождения.