- PVSM.RU - https://www.pvsm.ru -

Возможности Python 3, достойные того, чтобы ими пользовались

Многие программисты начали переходить со второй версии Python на третью из-за того, что уже довольно скоро поддержка Python 2 будет прекращена [1]. Автор статьи, перевод которой мы публикуем, отмечает, что основной объём Python 3-кода, который ему доводилось видеть, выглядит как код со скобками, написанный на Python 2. По его словам, он и сам грешит чем-то подобным [2]. Здесь он приводит примеры некоторых замечательных возможностей, доступных лишь тем, кто пользуется Python 3. Он надеется, что эти возможности облегчат жизнь тем, кто о них узнает.

Возможности Python 3, достойные того, чтобы ими пользовались - 1 [3]

Все примеры, приведённые в этом материале, написаны с использованием Python 3.7. В описании каждой возможности имеются сведения о минимальной версии Python, необходимой для её применения.

Форматные строки (3.6+)

Без использования строк сложно написать что-то полезное на любом языке программирования. Но для эффективной работы со строками разработчику нужны удобные инструменты. Такие инструменты, которые позволяют оперировать сложными структурами, не теряя при этом душевного равновесия. Большинство Python-разработчиков пользуется методом format:

user = "Jane Doe"
action = "buy"
log_message = 'User {} has logged in and did an action {}.'.format(
  user,
  action
)
print(log_message)
# User Jane Doe has logged in and did an action buy.

Python 3, наряду с методом format, поддерживает форматные строки (f-strings [4], f-строки). Они представляют собой гибкий инструмент для выполнения различных манипуляций со строками. Вот как выглядит предыдущий пример, переписанный с использованием форматных строк:

user = "Jane Doe"
action = "buy"
log_message = f'User {user} has logged in and did an action {action}.'
print(log_message)
# User Jane Doe has logged in and did an action buy.

Модуль pathlib (3.4+)

Форматные строки — замечательная технология, но для работы с некоторыми строками, вроде путей к файлам, созданы специальные инструменты, сильно упрощающие манипуляции с ними. В Python 3 имеется модуль pathlib [5], который представляет собой удобную абстракцию для работы с путями к файлам. Если вы пока не уверены в полезности этого модуля для решения ваших задач — взгляните на этот [6] материал.

from pathlib import Path
root = Path('post_sub_folder')
print(root)
# post_sub_folder
path = root / 'happy_user'
# Делаем путь абсолютным
print(path.resolve())
# /home/weenkus/Workspace/Projects/DataWhatNow-Codes/how_your_python3_should_look_like/post_sub_folder/happy_user

Аннотации типов (3.5+)

Что лучше — статическая или динамическая типизация? Пожалуй, почти каждый программист имеет собственный ответ на этот непростой вопрос. Я оставляю на усмотрение читателей то, как именно они типизируют свои программы. Но я считаю, что всем полезно будет хотя бы знать о том, что Python 3 поддерживает аннотации типов [7].

def sentence_has_animal(sentence: str) -> bool:
  return "animal" in sentence
sentence_has_animal("Donald had a farm without animals")
# True

Перечисления (3.4+)

Python 3 поддерживает, благодаря классу Enum, простой механизм работы с перечислениями [8]. Перечисления удобно использовать для хранения списков констант. Константы, в противном случае, оказываются беспорядочно разбросанными в коде.

from enum import Enum, auto
class Monster(Enum):
    ZOMBIE = auto()
    WARRIOR = auto()
    BEAR = auto()
    
print(Monster.ZOMBIE)
# Monster.ZOMBIE

Из документации [9] по Python 3 можно узнать о том, что перечисление — это набор символических имён (членов), привязанных к уникальным, неизменным значениям. Члены одного перечисления можно сравнивать на идентичность. Перечисления можно обходить.

for monster in Monster:
    print(monster)
# Monster.ZOMBIE
# Monster.WARRIOR
# Monster.BEAR

Встроенный LRU-кэш (3.2+)

В наши дни механизмы кэширования применяются практически во всех программных и аппаратных системах. Python 3 значительно упрощает кэширование благодаря декоратору lru_cache [10], который реализует алгоритм LRU-кэширования (Least Recently Used [11]).

Ниже показана функция, которая вычисляет числа Фибоначчи. Эта функция вынуждена по много раз выполнять одни и те же операции в ходе рекурсивных вызовов. В результате оказывается, что её производительность можно улучшить благодаря кэшированию.

import time
def fib(number: int) -> int:
    if number == 0: return 0
    if number == 1: return 1
    
    return fib(number-1) + fib(number-2)
start = time.time()
fib(40)
print(f'Duration: {time.time() - start}s')
# Duration: 30.684099674224854s

Теперь используем lru_cache для оптимизации этой функции (такая техника оптимизации называется мемоизацией [12]). В результате время выполнения функции, которое раньше измерялось секундами, теперь измеряется наносекундами.

from functools import lru_cache
@lru_cache(maxsize=512)
def fib_memoization(number: int) -> int:
    if number == 0: return 0
    if number == 1: return 1
    
    return fib_memoization(number-1) + fib_memoization(number-2)
start = time.time()
fib_memoization(40)
print(f'Duration: {time.time() - start}s')
# Duration: 6.866455078125e-05s

Распаковка итерируемых объектов (3.0+)

При распаковке [13] итерируемых объектов можно использовать переменные, перед именами которых ставят звёздочку. В такие переменные попадает всё то, что не попало в другие переменные. Так, в следующем примере в переменные head и tail попадают первое и последнее значения из списка, сформированного командой range(5). В переменную body попадает всё то, что находится между первым и последним значением.

head, *body, tail = range(5)
print(head, body, tail)
# 0 [1, 2, 3] 4
py, filename, *cmds = "python3.7 script.py -n 5 -l 15".split()
print(py)
print(filename)
print(cmds)
# python3.7
# script.py
# ['-n', '5', '-l', '15']
first, _, third, *_ = range(10)
print(first, third)
# 0 2

Классы данных (3.7+)

В Python 3 появились классы данных (data classes [14]). Они дают программисту достаточно большую свободу действий. Их можно использовать для уменьшения объёма шаблонного кода. Дело в том, что декоратор dataclass автоматически генерирует специальные методы, такие как __init__() и __repr__(). В официальном тексте соответствующего предложения [15] они описаны как «изменяемые именованные кортежи со значениями по умолчанию». Вот пример создания класса без использования декоратора dataclass:

class Armor:
    
    def __init__(self, armor: float, description: str, level: int = 1):
        self.armor = armor
        self.level = level
        self.description = description
                 
    def power(self) -> float:
        return self.armor * self.level
    
armor = Armor(5.2, "Common armor.", 2)
armor.power()
# 10.4
print(armor)
# <__main__.Armor object at 0x7fc4800e2cf8>

Вот то же самое, но уже написанное с применением dataclass:

from dataclasses import dataclass
@dataclass
class Armor:
    armor: float
    description: str
    level: int = 1
    
    def power(self) -> float:
        return self.armor * self.level
    
armor = Armor(5.2, "Common armor.", 2)
armor.power()
# 10.4
print(armor)
# Armor(armor=5.2, description='Common armor.', level=2)

Поддержка папок пакетов без файла __init__.py (3.3+)

Один из способов структурирования Python-кода заключается в использовании пакетов [16] (пакеты размещаются в папках, в которых есть файл __init__.py). Вот пример из официальной документации:

sound/                          Пакет верхнего уровня
      __init__.py               Инициализация пакета sound
      formats/                  Подпакет для преобразования форматов файлов
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Подпакет для звуковых эффектов
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Подпакет для фильтров
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

При использовании Python 2 в каждой из папок, упомянутых в примере, должен был быть файл __init__.py. Благодаря этому файлу папка воспринимается в виде Python-пакета. В Python 3, с появлением возможности Implicit Namespace Packages [17], наличие в папках подобных файлов больше не является обязательным.

sound/                          Пакет верхнего уровня
      __init__.py               Инициализация пакета sound
      formats/                  Подпакет для преобразования форматов файлов
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Подпакет для звуковых эффектов
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Подпакет для фильтров
              equalizer.py
              vocoder.py
              karaoke.py
              ...

Надо отметить, что на самом деле всё не так просто. А именно, в соответствии с этой [18] официальной спецификацией, файл __init__.py всё ещё нужен для обычных пакетов. Если убрать его из папки, то пакет превращается в так называемый пакет пространства имён [19], к которому применяются дополнительные ограничения.

Итоги

В этом материале рассмотрены далеко не все интересные возможности Python 3, но мы надеемся, что вы нашли здесь что-то полезное. Код примеров можно найти в этом [20] репозитории.

Уважаемые читатели! Какие возможности Python 3 вы добавили бы в приведённый здесь список?

Возможности Python 3, достойные того, чтобы ими пользовались - 2 [21]

Автор: ru_vds

Источник [22]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/razrabotka/318254

Ссылки в тексте:

[1] прекращена: https://pythonclock.org/

[2] чем-то подобным: https://datawhatnow.com/introduction-web-scraping-python/

[3] Image: https://habr.com/ru/company/ruvds/blog/452564/

[4] f-strings: https://www.python.org/dev/peps/pep-0498/

[5] pathlib: https://docs.python.org/3/library/pathlib.html

[6] этот: https://treyhunner.com/2018/12/why-you-should-be-using-pathlib/

[7] аннотации типов: https://docs.python.org/3/library/typing.html

[8] перечислениями: https://docs.python.org/3/library/enum

[9] документации: https://docs.python.org/3/library/enum.html

[10] lru_cache: https://docs.python.org/3/library/functools.html#functools.lru_cache

[11] Least Recently Used: https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)

[12] мемоизацией: https://en.wikipedia.org/wiki/Memoization

[13] распаковке: https://www.python.org/dev/peps/pep-3132/

[14] data classes: https://docs.python.org/3/library/dataclasses.html

[15] предложения: https://www.python.org/dev/peps/pep-0557/

[16] пакетов: https://docs.python.org/3/tutorial/modules.html#packages

[17] Implicit Namespace Packages: https://www.python.org/dev/peps/pep-0420/

[18] этой: https://www.python.org/dev/peps/pep-0420/#specification

[19] пакет пространства имён: https://packaging.python.org/guides/packaging-namespace-packages/#native-namespace-packages

[20] этом: https://github.com/Weenkus/DataWhatNow-Codes/blob/master/things_you_are_probably_not_using_in_python_3_but_should/python%203%20examples.ipynb

[21] Image: https://ruvds.com/ru-rub/#order

[22] Источник: https://habr.com/ru/post/452564/?utm_source=habrahabr&utm_medium=rss&utm_campaign=452564