Diversity w polskim IT
John D K
John D KFounder @ Softhints

Omówienie nowości w Pythonie 3.8

Sprawdź omówienie najważniejszych i ciekawych nowych funkcji i zmian, które pojawiły się w Pythonie 3.8.
14.11.20196 min
Omówienie nowości w Pythonie 3.8

Mija miesiąc od premiery Pythona 3.8. A oto krótki i praktyczny przewodnik po najważniejszych/interesujących nowych funkcjach/zmianach wprowadzonych w Pythonie 3.8. 


Tutaj znajdziesz oficjalną dokumentację:


Jeśli chcesz pobawić się nowymi funkcjami bez instalowania najnowszego Pythona 3.8, możesz to zrobić tutaj: Python 3.8 (pre-release)

Krótki film przedstawiający nowe funkcje obejrzysz tutaj.

Przegląd

Python 3.8 przychodzi z nowym operatorem warlus, parametrami wyłącznie pozycyjnymi oraz wieloma innymi interesującymi nowościami, takimi jak: wiele zmian języka, jeden nowy moduł i ulepszenia pozostałych. Pojawiły się również optymalizacje, zmiany w API C. Niektóre metody są już przestarzałe.

Osobiście nie zamierzam przestawiać się na najnowszą wersję w najbliższym czasie. Nie było żadnej przełomowej zmiany w tej wersji, która by mnie do tego zachęciła albo zmusiła. Niektóre funkcje mogą być zwodne i powodować zawiłości przy pisaniu, dlatego póki co będę się trzymał wersji 3.7.

Nowe funkcje

Dla mnie najciekawsze nowe funkcje to:

  • Operator Walrus
  • Parametry wyłącznie pozycyjne
  • f-strings support =


Operator Walrus :=

W skrócie, jest to nowy operator, który ma być używany do przypisania lub wyrażeń nazwanych. Oficjalny opis z PEP:

To propozycja stworzenia sposobu przypisywania do zmiennych w wyrażeniu za pomocą notacji NAME: = expr.

Jeśli zastanawiasz się, skąd ta nazwa:

Jest pieszczotliwie znany jako „operator mors” ze względu na podobieństwo do oczu i kłów morsa.

Najlepszym sposobem na zrozumienie nowego operatora jest spojrzenie na kilka przykładów:


Unikaj powtarzanych wywołań wyrażeń

num = 3
if (n := num % 2) != 0:
    print(f"Number is not even: number: {num}, remainder: {n}")


Rezultat:

Number is not even: number: 3, remainder: 1     


Wyrażenie regularne

import re, datetime
text = 'The latest version of python 3.8 is released on 14-10-2019 .. '

# before walrus
whole_match = re.findall(r'[\d]{1,2}-[\d]{1,2}-[\d]{4}', text)
if whole_match:
    print(whole_match)

# after walrus
if (found_date := re.findall(r'[\d]{1,2}-[\d]{1,2}-[\d]{4}', text)):
    print(found_date)


Wyrażenia listowe (list comprehension)

Jeśli wartość obliczona w warunku filtrowania jest również potrzebna w treści wyrażenia:

numbers = [3, '0', 0, '6', 2, 1, 0, '7']
print([final_number for number in numbers if (final_number := int(number)) != 0])


Rezultat:

[3, 6, 2, 1, 7]


Błędy i użycie

Czasami operator walrus może wprowadzać w błąd i prowadzić do wątpliwości lub błędów. Dobrym pomysłem może być ograniczenie użycia go tylko do prostych przypadków i wyrażeń, aby unikać problemów z czytelnością kodu.

Poniżej wyrażenia listowe i operator walrus, który prowadzi do błędu:

numbers = [3, 0, 0, '6', 2, 1, 0, '7']
[number for number in numbers if (number := int(number)) != 0]
SyntaxError: assignment expression cannot rebind comprehension iteration variable 'number'


Innym interesującym przypadkiem i możliwym nadużyciem jest:

if (I := he ) or ( you := me ) and ( we := alltogether):
    I = walrus


Więcej o operatorze przeczytasz tutaj: I walrus. Pełen opis znajdziesz tutaj: PEP 572.

Parametry wyłącznie pozycyjne (positional-only parameters)

Wprowadzono nową składnię parametrów funkcji - /. Oto przykładowa funkcja:

def f(a, /, b, *, c):
    print(a, b, c)


W tej funkcji parametr a jest tylko pozycyjny i nie może być argumentem słowa kluczowego. Z drugiej strony, c musi być argumentem słowem kluczowym.

Sprawdźmy kilka przykładów.

Prawidłowe wywołanie tej funkcji:

f(10, 20, c=30)


Rezultat:

10 20 30


Błędy

TypeError: f() takes 2 positional arguments but 3 were given

f(10, 20, 30) # c must be a keyword argument


Efekt:

TypeError: f() takes 2 positional arguments but 3 were given


TypeError: f() otrzymał kilka argumentów wyłącznie pozycyjnych, przekazanych jako argumenty słów kluczowych: a

f(a=10, b=20, c=30)   # a cannot be a keyword argument


Efekt:

TypeError: f() got some positional-only arguments passed as keyword arguments: 'a'


Parametry wyłącznie pozycyjne mogą być używane na dwa sposoby. Ich nazwy nie są wyświetlane jako dostępne słowa kluczowe, co czyni je dostępnymi dla kwargs:

def f(x, /, **kwargs):
    print(x, kwargs)

f(10, x=1, y=2)         # x is used in two ways
10 {'x': 1, 'y': 2}


Więcej informacji znajdziesz tutaj: Python Positional-Only Parameters

f-strings wspiera = by tworzyć somodokumentujące wyrażenia oraz poprawić debugowanie

W wersji 3.8 dodano wsparcie dla = dla f-strings. Tak więc:

f'{version=} is released on {release_date=}'

Będzie reprezentowany jako:

version='3.8' is released on release_date=datetime.date(2019, 10, 14)

Więcej przykładów

from datetime import date

version = '3.8'
release_date = date(2019, 10, 14)
python_one_version = date(1994, 1, 1)

since_python_one = release_date - python_one_version

print(f'{version=} is released on {release_date=}')
print(f'{version=} day of Release {python_one_version.day=:02d}')
print(f'{version=} day of Release {since_python_one.days=:,d}')


Rezultat:

version='3.8' is released on release_date=datetime.date(2019, 10, 14)
version='3.8' day of Release python_one_version.day=01
version='3.8' day of Release since_python_one.days=9,


Używane w poprzednich wersjach Pythona, takich jak Python 3.7, powodują:

  File "<fstring>", line 1
    (version=)
            ^
SyntaxError: invalid syntax


Główną ideą jest:

#(really) bad old days this was pretty wordy:
print "foo=", foo, "bar=", bar

#f-strings make this slightly nicer to type:
print(f"{foo=} {bar=}")


Rezultat:

foo=datetime.date(2019, 10, 14) bar=1

Inne nowe funkcje

Niektórych funkcji nie da się łatwo wyjaśnić przykładami, więc po prostu je wymienię:

  • Zrównoleglona pamięć podręczna w systemie plików dla plików ze skompilowanym kodem bajtowym
  • Wersja debugowa używa tego samego ABI co główne wydanie
  • PEP 578: Python Runtime Audit Hooks
  • PEP 587: Python Initialization Configuration
  • Vectorcall: protokół do szybkich wywołań dla CPythona
  • Nowy protokół Pickle 5

Inne zmiany języka

W tej sekcji jest kilka interesujących zmian, które mogą wymagać Twojej uwagi podczas migracji ze starszej wersji Pythona do wersji 3.8:

  • wyrażenie continue jest teraz dozwolone w klauzuli finally
  • nowa metoda as_integer_ratio() dla typów bool, int i Fraction
x = 7
print(x.as_integer_ratio())


Rezultat:

(7, 1)
  • Dodano wsparcie dla escape'owania przez \N{name} w wyrażeniach regularnych
import re

notice = 'Copyright © 2019'
copyright_year_pattern = re.compile(r'\N{copyright sign}')
print(copyright_year_pattern.search(notice).group(0))


To spowoduje błąd we wcześniejszych wersjach:

sre_constants.error: bad escape \N at position 0
  • Dict i dictviews są teraz iterowalne w odwróconej kolejności wstawiania za pomocą reversed()
  • Ostrzeżenia, gdy w kodzie brakuje przecinka, np. [(1, 2) (3, 4)]


Wcześniej:

File ".code.tio", line 1, in <module>
    [(1, 2) (3, 4)]
TypeError: 'tuple' object is not callable


Teraz:

SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
  [(1, 2) (3, 4)]
Traceback (most recent call last):
  File ".code.tio", line 1, in <module>
    [(1, 2) (3, 4)]
TypeError: 'tuple' object is not callable
  • Operacje arytmetyczne między podklasami obiektów datetime.date lub datetime.datetime i datetime.timedelta zwracają teraz instancję podklasy, a nie klasę podstawową.


Np.:

from datetime import datetime, timezone

class DateTimeSubclass(datetime):
    pass

dt = DateTimeSubclass(2012, 1, 1)
dt2 = dt.astimezone(timezone.utc)

print(type(dt))
print(type(dt2))


W Pythonie 3.8

<class '__main__.DateTimeSubclass'>
<class '__main__.DateTimeSubclass'>


Wcześniej:

<class '__main__.DateTimeSubclass'>
<class 'datetime.datetime'>
  • Gdy przerwie się interpreter Pythona przez Ctrl-C (SIGINT) i wynikowy wyjątek KeyboardInterrupt nie zostanie złapany, proces Pythona kończy się za pośrednictwem sygnału SIGINT -
  • Wyrażenia słownikowe (dict comprehensions) zostały zsynchronizowane z literałami w dict, dzięki czemu klucz jest obliczany pierwszy, a wartość druga
from unicodedata import normalize
names = ['Löwis', 'Łukasz', 'Dörwald']
print({(n := normalize('NFC', name)).casefold() : n for name in names})


Rezultat:

{'löwis': 'Löwis', 'łukasz': 'Łukasz', 'dörwald': 'Dörwald'}

Podsumowanie

Python 3.8 zawiera nowy operator przypisania, składnię parametrów funkcji oraz wiele innych ulepszeń i funkcji. Na pierwszy rzut oka niektóre zmiany wydają się nieco kontrowersyjne, ale jakby nie patrzeć, oferują nowe rozwiązanie starych problemów.

Przykłady omówione w tym artykule pokazują potencjał rozszerzenia zastosowania poza zakresem opisanym w PEP.

Gdy wersja zostanie dobrze zaadoptowana, przejdę na nią, aby zbadać jej pełny potencjał.


Oryginał tekstu w języku angielskim przeczytasz tutaj.

<p>Loading...</p>