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ę:
- What’s New In Python 3.8 - pełna dokumentacja
- Major new features of the 3.8 series, compared to 3.7 - krótki opis
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 klauzulifinally
- 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.