Моржовый оператор и новинки python 3.8

В python 3.8 появились новые возможности. Весь список смотреть тут: https://docs.python.org/3.8/whatsnew/3.8.html

Мы разберем в этой заметке:

  • assignment expressions := aka моржовый оператор,
  • управление аргументами функций и новые positional-only parameters,
  • f-strings и новый спецификатор =.

1. Моржовый оператор

По мотивам PEP 572.

Моржовый оператор := — разновидность оператора присваивания, с помощью которого можно давать имена подвыражениям в выражениях. Здесь мы не будем обсуждать удачность выбра синтаксиса, а просто рассмотрим 3 примера использования, где оператор может быть полезен:

  • в условиях,
  • в list comprehantion,
  • в lambda-выражениях.

На английском walrus (читать как ˈwȯl-rəs) operator.

Имена в условиях

Бывают ситуации, когда надо проверить доступность данных перед их использованием. Рассмотрим пример с функцией find, которая возвращает индекс начала подстроки в строке. Будем искать в это строке и будем возвращать подстроку начиная с искомого слова и до конца.

In [53]:
text = 'Пока кормят — ешь, пока поят — пей… Все как у людей…'

Будем искать слово, которого нет.

In [58]:
# Ищем слово, которого нет (ожидается, что ничего не напечатает)
res = text.find('где')
if res != -1:
    print(text[res:])

Будем искать слово, которое есть

In [59]:
# Ищем слово, которое есть (ожидается, что напечататье часть строки от слова как и до конца)
res = text.find('как')
if res != -1:
    print(text[res:])
как у людей…
In [60]:
# Упростим предыдущий код с помощью оператора :=
if (res := text.find('как')) != -1:
    print(text[res:])
как у людей…

Имена в list comprehantion

In [73]:
# Простейшая медленная функция возведения числа x в степень n
def pow(x,n):
    print('Вызов функции pow')
    res = 1
    while n:
        res *= x
        n -= 1
    return res

Создадим список квадратов чисел от 0 до 9, которые четные

In [75]:
# Простое прямое решение через list comprehantion
[pow(x, 2) for x in range(10) if pow(x, 2) % 2 == 0]
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Out[75]:
[0, 4, 16, 36, 64]
In [76]:
# Упростим предыдущий код с помощью walrus оператора
[p for x in range(10) if (p:=pow(x, 2)) % 2 == 0]
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Вызов функции pow
Out[76]:
[0, 4, 16, 36, 64]

Количество вызовов медленной функции pow(x,n) сократилось!

Имена в lambda

Если надо переиспользовать значения в lambda-функциях, то удобно применить := оператор. Рассмотрим комплексное число.

In [16]:
# Комплексное число
number = complex(3,4)
print(number)
(3+4j)
In [17]:
# Функция для вычисисления Re, Im и модуля комплексного числа
f = lambda z: (z.real, z.imag, (z.real**2+z.imag**2)**0.5)
f(number)
Out[17]:
(3.0, 4.0, 5.0)
In [18]:
# Функция для вычисисления Re, Im и модуля комплексного числа (упрощенная)
f = lambda z: (re:=z.real, im:=z.imag, (re**2+im**2)**0.5)
f(number)
Out[18]:
(3.0, 4.0, 5.0)

2. Позиционные аргументы

In [21]:
def func(a,b,c,d,e,f,g):
    print(a,b,c,d,e,f,g)
    
func(1,2,3,4,5,6,7)
1 2 3 4 5 6 7
In [22]:
def func(a=1,b=2,c=3,d=4,e=5,f=6,g=7):
    print(a,b,c,d,e,f,g)
    
func()
1 2 3 4 5 6 7
In [25]:
def func(a=1,*,b=2,c=3,d=4,e=5,f=6,g=7):
    print(a,b,c,d,e,f,g)
    
func(1, 2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-25-60e4b721e14d> in <module>
      2     print(a,b,c,d,e,f,g)
      3 
----> 4 func(1, 2)

TypeError: func() takes from 0 to 1 positional arguments but 2 were given
In [26]:
def func(a=1,*,b=2,c=3,d=4,e=5,f=6,g=7):
    print(a,b,c,d,e,f,g)
    
func(1, b=2)
1 2 3 4 5 6 7
In [27]:
def func(a=1,*,b=2,c=3,d=4,e=5,f=6,g=7):
    print(a,b,c,d,e,f,g)
    
func(a=1, b=2)
1 2 3 4 5 6 7
In [29]:
def func(a=1,/,b=2,c=3,d=4,e=5,f=6,g=7):
    print(a,b,c,d,e,f,g)
    
func(a=1, b=2)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-29-3a98167ae93a> in <module>
      2     print(a,b,c,d,e,f,g)
      3 
----> 4 func(a=1, b=2)

TypeError: func() got some positional-only arguments passed as keyword arguments: 'a'
In [30]:
def func(a=1,/,b=2,c=3,d=4,e=5,f=6,g=7):
    print(a,b,c,d,e,f,g)
    
func(1, b=2)
1 2 3 4 5 6 7
In [35]:
def length(obj,/):
    return len(obj)

# length(obj=[1,2,3]) <-- было бы избыточно
length([1,2,3])
Out[35]:
3

3. Метки в f-строках

In [40]:
name, age = 'Vlad', 23
print(f'{name}, {age}')
Vlad, 23
In [43]:
print(f'{age=}, {age=}')
age=23, age=23
In [46]:
print(f'{age=}, {age**2}')
age=23, 529
In [47]:
%load_ext watermark
%watermark -d -u -v -iv
last updated: 2019-10-23 

CPython 3.8.0
IPython 7.8.0
In [ ]: