Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Пишем на питоне так, чтобы все вас любили (или ненавидели)

Пишем на питоне так, чтобы все вас любили (или ненавидели)

Никита Соболев (CTO в Wemake.services) @ Moscow Python Meetup 67

"В своем докладе я хочу показать, как просто и как сложно писать простой код.

Чтобы разобраться: что сложно, а что просто; нам придется поговорить о разнице между м*&*ами и algebraic effects, SRP, о плюсах и минусах Typed DI, протоколах и приставучем IO. А еще поговорим про цвета функций, DDD, типы и клей для композиции.

Разложив все по полочкам, мы сможем написать первые несколько строк простого кода для большого проекта".

Видео: http://www.moscowpython.ru/meetup/67/python-love-or-hate/

Moscow Python Meetup

August 21, 2019
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. def recommended_books(books): book_names = [ book.name for book in books

    ] good_books = [ book_name for book_name in book_names if book_name not in ('Twilight', 'Learning Go Lang') ] recommended_books = [ find_similar(book_name) for book_name in book_names ] return recommended_books !5
  2. def recommended_books(books): book_names = [ book.name for book in books

    ] good_books = [ book_name for book_name in book_names if book_name not in ('Twilight', 'Learning Go Lang') ] recommended_books = [ find_similar(book_name) for book_name in book_names ] return recommended_books !6
  3. def recommended_books(books): book_names = [ book.name for book in books

    ] good_books = [ book_name for book_name in book_names if book_name not in ('Twilight', 'Learning Go Lang') ] recommended_books = [ find_similar(book_name) for book_name in book_names ] return recommended_books !7
  4. def recommended_books(books): book_names = [ book.name for book in books

    ] good_books = [ book_name for book_name in book_names if book_name not in ('Twilight', 'Learning Go Lang') ] recommended_books = [ find_similar(book_name) for book_name in book_names ] return recommended_books !8
  5. def recommended_books(books): book_names = [ book.name for book in books

    ] good_books = [ book_name for book_name in book_names if book_name not in ('Twilight', 'Learning Go Lang') ] recommended_books = [ find_similar(book_name) for book_name in book_names ] return recommended_books !9
  6. def recommended_books(books): book_names = [ book.name for book in books

    ] good_books = [ book_name for book_name in book_names if book_name not in ('Twilight', 'Learning Go Lang') ] recommended_books = [ find_similar(book_name) for book_name in book_names ] return recommended_books !17
  7. class RecommendedBooks(object): def __init__(self, book_repo, filter_api, cache): self._repo = book_repo

    self._api = filter_api self._cache = cache def __call__(self, books): book_names = self._repo.names_by_ids(books) good_books = self._api.by_preferences(book_names) recommended = self._cache.not_shown(good_books) return recommended !32
  8. Что понять ООП и писать нормально, нам нужно: • Прочитать

    уйму умных книг • Посмотреть на ужас Java и C++ !37
  9. Что понять ООП и писать нормально, нам нужно: • Прочитать

    уйму умных книг • Посмотреть на ужас Java и C++ • Понять прототипы из JS, протоколы из TypeScript !37
  10. Что понять ООП и писать нормально, нам нужно: • Прочитать

    уйму умных книг • Посмотреть на ужас Java и C++ • Понять прототипы из JS, протоколы из TypeScript • Выучить SmallTalk !37
  11. Что понять ООП и писать нормально, нам нужно: • Прочитать

    уйму умных книг • Посмотреть на ужас Java и C++ • Понять прототипы из JS, протоколы из TypeScript • Выучить SmallTalk • Поработать с акторами и Erlang. Выслушать тонну критики !37
  12. Что понять ООП и писать нормально, нам нужно: • Прочитать

    уйму умных книг • Посмотреть на ужас Java и C++ • Понять прототипы из JS, протоколы из TypeScript • Выучить SmallTalk • Поработать с акторами и Erlang. Выслушать тонну критики • Понять, что ООП нужно только для очень высокого уровня абстракции в некоторых типах систем !37
  13. FP

  14. class RecommendedBooks(object): def __init__(self, book_repo, filter_api, cache): self._repo = book_repo

    self._api = filter_api self._cache = cache def __call__(self, books): book_names = self._repo.names_by_ids(books) good_books = self._api.by_preferences(book_names) recommended = self._cache.not_shown(good_books) return recommended !40
  15. Типы курильщика Типы здорового человека uint double &mut str uint16_t

    std::boxed::Box<dyn Any + 'static> List[int] data Bool = False | True float Callable[[int], int] IO[Result[User, Exception]]
  16. class RecommendedBooks(object): def __init__( self, book_repo, filter_api, cache, ): self._repo

    = book_repo self._api = filter_api self._cache = cache def __call__(self, books): book_names = self._repo(books) good_books = self._api(book_names) recommended = self._cache(good_books) return recommended !48
  17. class RecommendedBooks(object): def __init__( self, book_repo: Callable[[List[int]], List[str]], filter_api, cache,

    ) -> None: self._repo = book_repo self._api = filter_api self._cache = cache def __call__(self, books: List[int]): book_names = self._repo(books) good_books = self._api(book_names) recommended = self._cache(good_books) return recommended !49
  18. class RecommendedBooks(object): def __init__( self, book_repo: Callable[[List[int]], List[str]], filter_api: Callable[[List[str]],

    List[Book]], cache, ) -> None: self._repo = book_repo self._api = filter_api self._cache = cache def __call__(self, books: List[int]): book_names = self._repo(books) good_books = self._api(book_names) recommended = self._cache(good_books) return recommended !50
  19. class RecommendedBooks(object): def __init__( self, book_repo: Callable[[List[int], List[str]], filter_api: Callable[[List[str]],

    List[Book]], cache: Callable[[List[Book]], List[Book]], ) -> None: self._repo = book_repo self._api = filter_api self._cache = cache def __call__(self, books: List[int]) -> List[Book]: book_names = self._repo(books) good_books = self._api(book_names) recommended = self._cache(good_books) return recommended !51
  20. @final @attr.dataclass(frozen=True, slots=True) class RecommendedBooks(object): _repo: Callable[[List[int]], List[str]] _api: Callable[[List[str]],

    List[Book]] _cache: Callable[[List[Book]], List[Book]] def __call__(self, books): book_names = self._repo(books) good_books = self._api(book_names) recommended_books = self._cache(good_books) return recommended_books !53
  21. !56

  22. import punq # Types: BookNamesByIds = Callable[[List[int]], List[str]] FilterPreferedBooks =

    Callable[[List[str]], List[Book]] NotShownBooks = Callable[[List[Book]], List[Book]] !56
  23. import punq # Types: BookNamesByIds = Callable[[List[int]], List[str]] FilterPreferedBooks =

    Callable[[List[str]], List[Book]] NotShownBooks = Callable[[List[Book]], List[Book]] # DI: container = punq.Container() container.register(BookNamesByIds, my_book_repo) container.register(FilterPreferedBooks, my_api) container.register(NotShownBooks, my_cache) !56
  24. import punq # Types: BookNamesByIds = Callable[[List[int]], List[str]] FilterPreferedBooks =

    Callable[[List[str]], List[Book]] NotShownBooks = Callable[[List[Book]], List[Book]] # DI: container = punq.Container() container.register(BookNamesByIds, my_book_repo) container.register(FilterPreferedBooks, my_api) container.register(NotShownBooks, my_cache) # Somewhere: container.resolve(RecommendedBooks)([1, 2, 3]) !56
  25. Callable[[List[int]], List[str]] • Выбросит ли она исключение? Да? Result[List[int], Exception]

    • Будет ли добиться в базу или http? Да? IO[List[int]] !62
  26. Callable[[List[int]], List[str]] • Выбросит ли она исключение? Да? Result[List[int], Exception]

    • Будет ли добиться в базу или http? Да? IO[List[int]] • Все вместе? Конечно! IO[Result[List[int], Exception]] !62
  27. class FetchUserProfile(object): def __call__(self, user: int) -> IO[Result['User', Error]]: return

    pipe( user_id, self._make_request, IO.lift(box(self._parse_json)), ) @impure @safe def _make_request(self, user_id: int) -> Response: ... @safe def _parse_json(self, response: Response) -> 'User': ... !64
  28. 65

  29. !67

  30. Полезные ссылки • https://guide.elm-lang.org/architecture/ • https://blog.ploeh.dk/2016/03/18/functional- architecture-is-ports-and-adapters/ • https://fsharpforfunandprofit.com/rop/ •

    https://www.destroyallsoftware.com/screencasts/ catalog/functional-core-imperative-shell • https://www.youtube.com/watch?v=o9pEzgHorH0 • https://sobolevn.me/ !72