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

Original author: Vinko Kodžoman
  • Translation
Многие программисты начали переходить со второй версии Python на третью из-за того, что уже довольно скоро поддержка Python 2 будет прекращена. Автор статьи, перевод которой мы публикуем, отмечает, что основной объём Python 3-кода, который ему доводилось видеть, выглядит как код со скобками, написанный на Python 2. По его словам, он и сам грешит чем-то подобным. Здесь он приводит примеры некоторых замечательных возможностей, доступных лишь тем, кто пользуется Python 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, 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, который представляет собой удобную абстракцию для работы с путями к файлам. Если вы пока не уверены в полезности этого модуля для решения ваших задач — взгляните на этот материал.

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 поддерживает аннотации типов.

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, простой механизм работы с перечислениями. Перечисления удобно использовать для хранения списков констант. Константы, в противном случае, оказываются беспорядочно разбросанными в коде.

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

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

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

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


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

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

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 для оптимизации этой функции (такая техника оптимизации называется мемоизацией). В результате время выполнения функции, которое раньше измерялось секундами, теперь измеряется наносекундами.

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+)


При распаковке итерируемых объектов можно использовать переменные, перед именами которых ставят звёздочку. В такие переменные попадает всё то, что не попало в другие переменные. Так, в следующем примере в переменные 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). Они дают программисту достаточно большую свободу действий. Их можно использовать для уменьшения объёма шаблонного кода. Дело в том, что декоратор dataclass автоматически генерирует специальные методы, такие как __init__() и __repr__(). В официальном тексте соответствующего предложения они описаны как «изменяемые именованные кортежи со значениями по умолчанию». Вот пример создания класса без использования декоратора 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-кода заключается в использовании пакетов (пакеты размещаются в папках, в которых есть файл __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, наличие в папках подобных файлов больше не является обязательным.

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
              ...

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

Итоги


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

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

RUVDS.com
837.24
RUVDS – хостинг VDS/VPS серверов
Share post

Comments 82

    +5
    Мне кажется, абсолютно необходимо упомянуть, что аннотации типов это не проверка типов, а подсказка компилятору.
    >>> def f(a: str):
    ...     print(a)
    ...     return
    ... 
    >>> f('a')
    a
    >>> f(1.0)
    1.0
    >>> import sys
    >>> sys.version
    '3.5.2 (default, Nov 12 2018, 13:43:14) \n[GCC 5.4.0 20160609]'
    
      +3
      Не только компилятору, но и всяким IDE
        +3
        Моя интуиция почему-то протестует против внесения в синтаксис языка фичей предназначеных для IDE. Слишком сильно связаны получаеются IDE и язык.
          +1
          а как комплиятор сейчас это использует? Насколько я читал, CPython пока никак не использует type annotation, а вот IDE уже вовсю
            0
            Сам компилятор в рантайме не использует, но можно подключить «runtime code evaluator» и он будет проверять данные в рантайме.
            Ссылка: the state of type hints in Python
            0
            Использование аннотаций, помимо прочего, достаточно сильно повышает читаемость кода. Так что можно считать это не только фичей для IDE.
              0
              Есть еще анализатор mypy, который умеет проверять соответствие типов
                0

                Для произвольного кода?


                Какова вообще выразительная сила тайпхинтов как системы типов?

                  0
                  Там есть Any, Union (sum type), дженерики и пр. Т.е. можно описать любой тип, даже сложную вложенную структуру или функцию. Можно создать свой кастомный тип.
                  В общем Python списывает хорошее у Haskell ещё начиная со списковых выражений.
                  подробнее здесь: docs.python.org/3/library/typing.html
                    0
                    дженерики

                    А там накладывать требования на типы можно? Ну, например, чтобы у меня дженерик был не поверх любого типа T, а поверх только U[T] для фиксированного (или нет) U?


                    В общем Python списывает хорошее у Haskell ещё начиная со списковых выражений.

                    Тайпклассы, экзистенциальные типы, кусочки зависимых типов, kind polymorphism?

                      0
                      чтобы у меня дженерик был не поверх любого типа T, а поверх только U[T] для фиксированного (или нет) U?

                      Кажется, да.

                      Много можно чего ещё скопировать из Haskell (и раздуться похлеще монстра C++), но задачи такой, как я понимаю, не стоит. Я просто имел в виду, что разработчики Python в сторону Haskell смотрят внимательно и заимствуются фичи оттуда давно, потому что list comprehensions в Python с 2000 года.
                        0
                        Много можно чего ещё скопировать из Haskell (и раздуться похлеще монстра C++)

                        Ну это вы зря так, сам язык (если на библиотеку не смотреть) вообще мелкий, меньше сотни страниц ЕМНИП.

                        0

                        Можно указать типы при создании TypeVar и будет он co-, contra- или in- вариантным. К сожалению, пока этот механизм в том же mypy и pycharm работает с проблемами

              +8
              Python это не компилируемый язык.
              Код python исполняется интерпретатором, а он в свою очередь все эти аннотации типов полностью игнорирует, что вы же в своём примере и продемонстрировали.

              Аннотации это для чего угодно, но не для исполнения кода: для читаемости, для подсказок в IDE, для статического анализа (mypy тот же самый), etc.
                +1
                Ну здравствуйте, вроде как всю жизнь код питон компилируется в промежуточный байт-код и этот байт код затем исполняется.
                  +1
                  Ну объектные файлы же не продуцируются. Слинковать с другими объектными файлами ничего нельзя.

                  Байт-код все равно исполняется виртуальной машиной.
                    0
                    С точки зрения типов, компилятор мог бы проверять типы на этапе компиляции байт кода. Таким образом, можно проверять код до его запуска. У чистого интерпретатора, без этапа компиляции такой возможности нет, в случае ошибки типа он все-равно должен закончить выполнение.
                    Т.е. у чистого интерпретатора мало смысла в проверке типов.
                    Я то сам, дне думаю, что для динамического языка не стоит внедрять обязательную проверку типов. Ну если только в качестве опции в виде билбиотеки. Не в ядре.
                    Я за то, чтобы сохранять ядро как можно более компактным.
                    • UFO just landed and posted this here
                      0
                      С такой логикой любой язык можно назвать интерпретируемым, ведь в конечном итоге всё интерпретируется процессором.
                        0
                        При всём уважении к Вашему юзернэйму, это так не называется.
                          0
                          Не называется, но если следовать логике lorc (при которой если язык компилируется в нечто, что далее интерпретируется, то сам язык считается интерпретируемым), все языки можно назвать интерпретируемыми.
                            0
                            Нет, это не моя логика. Пожалуйста, не надо приписывать мне то, чего я не говорил.
                              –1
                              А это чьи слова:
                              Байт-код все равно исполняется виртуальной машиной.

                              И это в ответ на утверждение, что питон компилируется. Из контекста я понял, что вы считаете его интерпретируемым. Или я ошибаюсь?
                                +1
                                Да, я продолжаю считать питон интерпретируемым. И буду считать его таким, пока не смогу получить из программы на питоне объектный файл, скормить его ld и слинковать его с моей программой на С.
                                  0
                                  Из программы на питоне можно получить .pyc файл. И при этом ни строчки из этой программы не исполнить.
                                    0
                                    Скажите, .pyc-файл исполняется непосредственно на процессоре, без вспомогательных прокладок?

                                    Вот когда начнёт исполняться — тогда и будет компилируемым.
                                      0
                                      Но байткод-то в результате чего получается? В результате компиляции питонокода.

                                      Машкод тоже может не исполнятся непосредственно на процессоре. В современных процах он собственно и не исполняется, а транслируется в микрокоды, которые затем исполняются.
                                        0
                                        Компилируемый язык программирования — язык программирования, исходный код которого преобразуется компилятором в машинный код.

                                        В машинный.
                                        p.s. Тут какой-то спор о терминах, которые уже давно устоялись.

                                          0
                                          Кроме того, существуют реализации языков, которые компилируют исходный текст программы в байт-код, который затем либо интерпретируется, либо выполняется т. н. JIT-компилятором (или виртуальной машиной). Это привносит ещё больше неясности в вопрос о том, где именно должна быть проведена граница между компилируемым языком и языком интерпретируемым.


                                          Можно и поспорить ;)
                          0
                          Нет. Компилятор производит объектные файлы, которые потом линкуются в исполняемый файл. Который потом может быть загружен в память и исполнен процессором. Это вроде бы обычный способ определять компилируемый язык.

                          И нет, если вы запакуете виртуальную машину и байт-код в один файл, то это все равно не будет скомпилированной программой. Ваша программа (а не ВМ) должна быть переведена в машинные коды, которые смогут исполняться процессором целевой платформы.
                            +1
                            Во-первых, компилятор не обязательно генерирует объектный файл, он может генерировать и файл с байт-кодом, или сразу исполняемый файл, или вообще простой бинарный файл являющийся сырым образом программы и её данных (ещё возможен вариант генерации программы на другом языке, но это принято называть транспилером).
                            Во-вторых, компилятор может вообще не генерировать никаких файлов, а компилировать программу в памяти, снаружи он будет подобен интерпретатору, но компилятором от этого быть не перестанет.

                            если вы запакуете виртуальную машину и байт-код в один файл, то это все равно не будет скомпилированной программой

                            Здрасьте, а байткод откуда взялся, если не был скомпилирован?

                            Я бы называл питон компилируемым в байт-код. Но да, сам байткод при этом интерпретируется (как и машинный код).
                              +1
                              >>Я бы называл питон компилируемым в байт-код. Но да, сам байткод при этом интерпретируется (как и машинный код).

                              После последней фразы могу запостить только эту ссылку. Финиш, конечно:
                              www.youtube.com/watch?v=LLk9_EH6Pfo
                                0
                                Что тут для вас «поворот»?
                    0
                    Эм в этом примере наоборот же получается, чистый питон игнорирует аннотации.
                      0
                      Ну да, я это и хотел показать. Что чистый питон не проверяет типы. Если бы проверял, он что-то бы сделал, например выбросил бы исключение.
                      0
                      Ну скажем так, это не настоящая проверка типов, но кое-где они проверяются. Например dataclass не создает параметр для __init__ у которого не проставлен тип данных или добавленная перегрузка overload, из того же пакета typing, вообще не работает. Очень надеюсь в будущих выпусках разработчики усилят это направление.
                        0
                        Для меня большой вопрос, нужно ли тащить статическую проверку типов в рантайм питона. Возможно просто я не сталкиваюсь с необходимостью статической проверки, но лично меня вполне устраивает дак тайпинг в питоне. Если нужна строгая типизация можно просто взять другой язык. Ну хотя бы тот же Cython. Или Си или C++.
                        Если все тащить в одну кучу получится еще один C++, точнее получим все недостатки C++, без его достоинств.
                        В моей практике ошибки с типами так редки, что ИМХО не стоит городить огород.
                          +1

                          Ну мне кажется что все таки неплохо иметь возможность при необходимости использовать типизацию (пусть даже если для того что бы она работала в рантайме надо подключить какой ни будь enforce) в основном для себя языке программирования.

                            +2
                            В виде подключаемых библиотек я готов мириться. Пусть будет, для любителей. Но только не в ядре языка.
                            По моему это просто мода. Лично я накушался строгой типизации в C++. А так сначала можно затащить строгую типизацию в питон, а потом без дизайн паттернс ни шагу ступить нельзя будет.
                              0
                              Без аннотации типов командой больше 5 человек разрабатывать очень трудно. Совершенно непонятно что тебе пробросили через 3 функции. Или могу я вызвать чужой метод и отправить в него set вместо list. это нужно еще и чужой код просмотреть прежде чем использовать.

                              Ну и уж совсем мечты это нормально предсказание типов для LLVM и для компилятора в WebAssemly, а то питон сейчас жив одним ML
                            0
                            строгая статическая типизация
                              +1
                              статическую проверку типов в рантайм

                              Статическая проверка не может быть в рантайме, она выполняется на этапе компиляции, проверки в рантайме называются динамическими.

                              Если нужна строгая типизация можно просто взять другой язык

                              Типизация в питоне, кстати, строже чем в большинстве скриптовых. Хоть функции и принимают любые объекты, но вольностей, типа складывания строки с числом он не позволяет, и неявных приведений типов (как в js — лишь бы что-то выполнить) он не делает.
                                0
                                Хоть функции и принимают любые объекты, но вольностей, типа складывания строки с числом он не позволяет, и неявных приведений типов (как в js — лишь бы что-то выполнить) он не делает.

                                >>> 3 == "10"
                                False

                                Сравнивать значения разных типов он, однако, вполне себе позволяет.

                                  +1
                                  Потому что это осмысленная операция, и, в отличие от всяких JS и PHP, питон не будет пытаться привести их к одному типу, т.е. для разных типов всегда будет False.
                                    0
                                    $ php -r 'var_dump(3 === "10");'
                                    bool(false)
                                      0
                                      Ваше === — это костыль для замены == с его глупым поведением.
                                        –2
                                        Невежа. Тьфу на вас.
                                          0
                                          Просвятите, какая необходимость иметь в вашем языке оператор ==?
                                            0
                                            Мой язык — русский, в нём нет этого оператора. Кто касается ПХП, то достаточно вспомнить историю языка — простая работа с вебом, данные из форм приходят нетипизированно.
                                      0

                                      Для разных типов должен кидаться экзепшон, потому что никакие два значения разных типов никогда не могут быть равны. Если я сравниваю значения разных типов, то у меня ошибка в логике. John Major equality — плохая штука.


                                      Иными словами, False в 3 == 4 и False в 3 == "4" имеют принципиально разную семантику.

                                        0
                                        Это позволяет избежать лишних проверок типов руками. Обычное дело для функций, принимающих параметры различного типа.
                                          0

                                          А зачем параметры различного типа сравнивать? Они же различного типа.

                                            0
                                            С динамической типизацией тип параметров заранее может быть не известен же.
                                              0

                                              Со статической в каком-то смысле тоже не обязан. Обычное гомогенное равенство вполне себе полиморфно.

                                      –1
                                      >> 3 == "10"
                                      False
                                      Правильнее так:
                                      >>> print( 10 == "10")
                                      False

                                      Если числа разные — непонятно, что есть False
                                      Без print не работает
                                      >>>  3 == "10"
                                        File "<stdin>", line 1
                                          3 == "10"
                                          ^
                                      IndentationError: unexpected indent

                                        +2
                                        > Без print не работает

                                        Конечно, если пробел впереди воткнуть — работать не будет.

                                        >>> 3 == "10"
                                        False
                                        >>>  3 == "10"
                                          File "<stdin>", line 1
                                            3 == "10"
                                            ^
                                        IndentationError: unexpected indent
                                        


                                        только print тут ни при чём.
                                    +1
                                    Clojure — интерпретируемый язык с динамической типизацией, но там есть библиотека spec, которая позволяет описать «схему» данных примерно как в Python, но в рантайме это всё проверяется на корректность. И также есть аннотации типов, которые позволяют указать тип Java и это влияет на производительность, потому что можно написать алгоритм, который будет работать только со списком Long, не задействуя рефлексию.
                                  0
                                  не компилятору, а статическому анализатору
                                    0
                                    Наверное лучше вообще убрать и компилятор и статический анализатор и просто написать; «это просто подсказки».
                                    0

                                    Также, type hints можно использовать, когда нам нужна явная привязка названия переменной к её типу. Например, есть библиотека pydantic, которая использует type hint'ы в датаклассах для маршаллинга и валидации схемы данных. Мы перешли на неё с marshmallow, очень удобно.

                                      0

                                      Стоит отметить, что pydantic использует свои классы вместо питоновских dataclasses, что может ограничивать применимость. Я в свободное время пилю библиотечку, которая может быть применима для уже существующих датаклассов без необходимости как-то их менять.

                                    0
                                    Python молодцы. Многие языки еще долго будут догонять.

                                    Еще бы добавить аннотаций для функционального программирования и будет еще круче
                                      –1
                                      Повторюсь. Я за то, чтобы сохранять ядро языка как можно более компактным. Чтобы его можно бы было всюду использовать с минимальными изменениями.
                                        +1
                                        По идее давно напрашивается модульный язык. Зачастую в энтерпрайзе надо чтобы программисты использовали одни и те же конструкции и не использовали какие-то другие. Надо научить язык отсекать лишние в конкретном проекте конструкции.
                                        0
                                        В модуле typing вроде есть Callable? Т.е. функции высшего порядка вполне могут работать с этими аннотациями.
                                          0
                                          аннотаций для функционального программирования

                                          Вы мне лучше скажите, какая аннотация должна быть у функции, принимающей совпадение по регулярке (тип _sre.SRE_Match)?
                                          +2
                                          Какие возможности Python 3 вы добавили бы в приведённый здесь список?
                                          asyncio и холиварный := (3.8+)
                                            0
                                            что сказать, язык будущего))

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

                                            и очень интересная фича LRU кэш, я чессно не знал и dict юзал
                                            кста вроде одинакого по скорости этот кэш и ручной через dict, кто нибудь протестил нормально скорость??

                                            имхо, в Питон версии 100500 + следует ввести:
                                            • инспекцию структуры любого обьекта с преттипринтом или лучше в визуальном представлении, когда берёшь неизвестный модуль, где документация плохая а все типы стёрты
                                            • обьединить нотацию для класса и dicta, inst['field'] эквивалентно inst.field

                                              интерпретировать сложные comprehention декларативно с оптимизаций и кешем где надо


                                              встроить без геморa частичные функции, с поддержкой интерпретатора заместо partial:

                                              def fn ( a,b,c)

                                              k = fn(1,2) #-> обьявление частичной функции k(c) c изсестными a,b,
                                              потом
                                              k[0] = 3 # безгеморная замена параметра на определённом месте




                                              сложные lambda обьявления с функционалом как обычный метод

                                              fib = lambda @lru_cache(maxsize=512), a: int ,b : int -> int : if number == 0: return 0
                                              if number == 1: return 1

                                              return fib_memoization(number-1) + fib_memoization(number-2)





                                              0
                                              кстати о f-string.
                                              оно и раньше так же примерно работало
                                              "Hello {user}!".format(**locals) 

                                              или **vars
                                                +1
                                                Все же не так красиво, да и по скорости f-string в разы* быстрее:
                                                Type            Trial 1                 Trial 2                 Trial 3
                                                %:              0.273233943000          0.268914790000          0.273714235000
                                                str.format():   0.7942503730000681      0.793637686999773       0.7926878570001463
                                                str.Template(): 3.3321329630002765      3.3256752329998562      3.315622544999769
                                                f-string:       0.1914799450000828      0.18900782099990465     0.19004946999984895
                                                Тестировалось на python 3.7, оригинал.
                                                  0
                                                  ну ясно дело оптимизировали и сделали изящнее.
                                                  0
                                                  оно и раньше так же примерно работало

                                                  Нет, не так же. Можно было только подставлять значения, а делать вычисления прямо внутри было нельзя (теперь в f-string можно). Так что теперь нужно быть осторожнее с такими строками. <sarkazm>Это почти как маленький php внутри python.</sarkazm>
                                                  Ко всему + то что сказал lega производительность в разы больше.


                                                  Примерные тесты
                                                  ~ $ python3.7 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())'
                                                  500000 loops, best of 5: 620 nsec per loop
                                                  ~ $ python3.7 -m timeit 'a = 1' 'b = "string"' 'f"Test strings formatting: {a} {b}"'
                                                  2000000 loops, best of 5: 157 nsec per loop
                                                  ~ $ python3.6 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())'
                                                  1000000 loops, best of 3: 0.599 usec per loop
                                                  ~ $ python3.6 -m timeit 'a = 1' 'b = "string"' 'f"Test strings formatting: {a} {b}"'
                                                  10000000 loops, best of 3: 0.142 usec per loop
                                                  ~ $ python2.7 -m timeit 'a = 1' 'b = "string"' '"Test strings formatting: {a} {b}".format(**locals())'
                                                  1000000 loops, best of 3: 0.435 usec per loop
                                                  0

                                                  Изменения крутые и полезные.
                                                  Очень хотелось бы что бы исправили концептуальные проблемы языка, даже если это означает python 4 и сломанную обратную совместимость.


                                                  Например те же импорты все ещё очень сложны для понимания новичками (особенно по сравнению с другими языками). При этом циркулярные импорты падают в рантайме с неадекватной ошибкой, а orm фреймворки вроде той же алхимии предлагают решать проблему просто — указывать название классов текстом *facepalm".

                                                    +1

                                                    К сожалению, я изучал питон очень давно и не могу 100% вспомнить какие у меня были проблемы. Но, кажется, импорты — это не та вещь, которая мне показалась непонятной.


                                                    Про циклические импорты действительно не мешало бы добавить текст более явный, хотя если посмотреть на стек это и глазами довольно легко понять.


                                                    В ОРМ циклические завимости надо решать не написанием названий классов текстом, а правильной расстановкой relation, мне кажется это в 95% случаев возможно (если я не прав — прошу привести пример).

                                                      0
                                                      циклическая зависимость легко может разрешаться интерпретатором без жалобы на якобы ошибки, зря что так не встраивают. Странное дело её не разруливает даже maven в своих проэктах…
                                                        +1

                                                        На мой взгляд, циклическая зависимость в большинстве — ошибка проектирования и должна исправляться переразбиением модулей или снижением связности кода (иногда — использованием DI, например).

                                                          +2
                                                          циклическая зависимость легко может разрешаться интерпретатором без жалобы на якобы ошибки
                                                          Как такая цикличекая зависимость может решиться интерпретатором?
                                                          # a.py
                                                          import b
                                                          value = b.value + 5
                                                          
                                                          # b.py
                                                          import a
                                                          value = a.value + 7
                                                          

                                                          PS: В питоне нормально решаются циклические зависимости, проблемы не в питоне, а «в руках».
                                                            0
                                                            её не разруливает даже maven в своих проэктах

                                                            Имхо, с ней намного проще получить в проекте одну и ту же зависимость с разными версиями. И долго искать причину фокусов в рантайме.

                                                        0
                                                        LRU-кэш полезная штука, но надо пользоваться с умом, т.к. он подходит только для идемпотентных функций(методов)
                                                          +1
                                                          Если говорить о Python 3 в целом, то в версии 3.8 у нас будут Assignment Expressions (PEP 572):
                                                          if x := True:
                                                               print(f'{x}')
                                                          # Выведет: True
                                                          

                                                          x = 2
                                                          print([y := x**x, y**2, y**3])
                                                          # Выведет: [4, 16, 64]
                                                          


                                                          И много разговоров про None-aware operators (PEP 505), значительно сокращающие код:
                                                          # Old
                                                          data = data if data is not None else []
                                                          # New
                                                          data = data ?? []
                                                          
                                                          # Old
                                                          if lst:
                                                              lst.append('string')
                                                          # New
                                                          lst?.append('string')
                                                          
                                                          # Old
                                                          if callable(foo):
                                                              foo()
                                                          # New
                                                          foo?()
                                                          
                                                          # Old
                                                          if x:
                                                             x + 1
                                                          # New
                                                          x? + 1
                                                          
                                                          # Old
                                                          result = a
                                                          try:
                                                              result = result.b
                                                          except AttributeError:
                                                              pass
                                                          else:
                                                              result = result.c
                                                              try:
                                                                  result = result.d
                                                              except AttributeError:
                                                                  pass
                                                              else:
                                                                  result = result.e
                                                          # New
                                                          a?.b.c?.d.e
                                                          

                                                          Однако PEP 505 сейчас в состоянии Deferred.

                                                          Ну и конечно следовало в статье упомянуть asyncio — это важная часть, по которой сделано много хорошей и качественно работы.
                                                            0
                                                            а ничего что по идее должно выбрасываться exception и исполнение прекращается, а тут проглатываются эксепшены и выдаётся None которое может стать ложноположительным?
                                                              +1

                                                              Используйте a.b.c.d.e и будет exception

                                                          Only users with full accounts can post comments. Log in, please.