Python 3.12 wprowadza wiele fascynujących zmian i ulepszeń, które sprawiają, że język staje się jeszcze bardziej potężny i elastyczny. W tym poście przyjrzymy się najciekawszym nowościom.
PEP 695: Składnia parametrów typów
Jednym z najważniejszych ulepszeń jest wprowadzenie nowej, bardziej zwięzłej i wyraźnej składni do tworzenia klas i funkcji generycznych, zaproponowanej w PEP 695. Ta zmiana upraszcza deklarowanie typów generycznych i aliasów typów, co czyni kod bardziej zrozumiałym i łatwiejszym w utrzymaniu. Na przykład, teraz możemy zdefiniować generyczną klasę listy w sposób znacznie bardziej bezpośredni:
class list[T]:
def append(self, element: T) -> None:
...
PEP 701: Ulepszenia f-stringów
PEP 701 rozszerza możliwości f-stringów, umożliwiając użycie w ich wnętrzu dowolnego wyrażenia Pythona, w tym komentarzy, wieloliniowych wyrażeń i znaków unicode. To ulepszenie znacznie zwiększa elastyczność f-stringów, umożliwiając tworzenie bardziej złożonych i czytelnych ciągów formatujących bez konieczności uciekania się do konkatenacji czy innych, mniej eleganckich rozwiązań.
Przykład poprzednich problemów
# python 3.11
>>> f'Magic wand: { bag['wand'] }'
^
SyntaxError: invalid syntax
>>> f'''A complex trick: {
... bag['bag'] # recursive bags!
... }'''
SyntaxError: f-string expression part cannot include '#'
Więcej przykładów w dokumentacji: https://peps.python.org/pep-0701/ - warto zajrzeć.
To duża i bardzo pozytywna zmiana, z potencjałem na znaczne uproszczenie kodu, którą świetnie podsumowuje dokumentacja: "You can place any valid Python expression inside an f-string expression."
PEP 684: GIL na interpreter
PEP 684 wprowadza koncepcję GIL (Global Interpreter Lock) przypisanego do każdego subinterpretera, co otwiera drzwi do lepszego wykorzystania wielowątkowości w Pythonie. Dzięki temu każdy subinterpreter może wykorzystać pełnię mocy wielordzeniowych procesorów bez wpływu na globalny GIL, co stanowi krok w kierunku poprawy współbieżności w Pythonie.
Ta zmiana raczej będzie transparentna dla programistów.
Ref: https://peps.python.org/pep-0684/
Nieśmiertelne obiekty
Nieśmiertelne obiekty w Pythonie 3.12 to istotna zmiana wewnętrzna mająca na celu poprawę wydajności poprzez zapobieganie aktualizacji liczników referencji dla określonych obiektów. Ta funkcja jest szczególnie przydatna dla niezmienialnych obiektów, takich jak True
, False
, None
i liczby całkowite o niskich wartościach. Poprzez oznaczenie tych obiektów jako nieśmiertelnych za pomocą specjalnej wartości licznika referencji (4294967295), mechanizm odśmiecania pamięci i licznik referencji Pythona nie modyfikują już podstawowej struktury pamięci tych obiektów, co pozwala na zachowanie ich niezmienności przez cały czas wykonania programu. Zmiana ta dotyczy głównie sposobu, w jaki środowisko wykonawcze Pythona zarządza pamięcią i cyklem życia obiektów, z potencjalnymi korzyściami we wdrożeniach wieloprocesowych poprzez zmniejszenie obciążenia pamięci i poprawę skalowalności (https://peps.python.org/pep-0683/, https://simonwillison.net/2023/Aug/26/understanding-immortal-objects-in-python-312/).
Wprowadzenie nieśmiertelnych obiektów było motywowane potrzebą rozwiązania problemów z wydajnością w dużych aplikacjach wieloprocesowych, takich jak te używane w Instagramie. Podejście to pozwala na lepsze współdzielenie pamięci między procesami poprzez minimalizację operacji "kopiowania przy zapisie", które występują, gdy aktualizowane są liczniki referencji współdzielonych obiektów. Prowadzi to do zmniejszenia wykorzystania prywatnej pamięci i zwiększenia udziału pamięci współdzielonej, przyczyniając się do ogólnych usprawnień wydajności w aplikacjach z intensywnymi wymaganiami dotyczącymi równoległości (https://engineering.fb.com/2023/08/15/developer-tools/immortal-objects-for-python-instagram-meta/).
Dekorator @override
Dekorator @override
w Pythonie 3.12 jest nowością, która ma na celu zwiększenie bezpieczeństwa typów w kodzie, umożliwiając programistom wyraźne oznaczanie metod w klasach pochodnych, które nadpisują metody w klasie bazowej. Ma to na celu pomoc w uniknięciu błędów, które mogą pojawić się podczas refaktoryzacji kodu, szczególnie w dużych bazach kodu, gdzie łatwo przeoczyć relacje między metodami różnych klas. W innych popularnych językach programowania, takich jak Java czy C++, istnieje podobna funkcjonalność, co pokazuje, że jest to sprawdzony sposób na zwiększenie czytelności i bezpieczeństwa kodu. PEP 698
Gdy statyczny analizator kodu napotka metodę ozdobioną dekoratorem @override
, traktuje to jako błąd typu, chyba że metoda ta faktycznie nadpisuje kompatybilną metodę lub atrybut w jakiejś klasie przodka. Użycie @override
ma na celu uczynienie nadpisania metod bardziej jawnych i zrozumiałych, zwłaszcza dla osób, które nie są zaznajomione z kodem. Dzięki temu, podczas czytania implementacji podklasy, można natychmiast zobaczyć, które metody nadpisują funkcjonalność w jakiejś klasie bazowej. Domyślnie, dekorator @override
jest opcjonalny, co oznacza, że istniejące bazy kodów, które go nie używają, będą działać jak wcześniej, bez dodatkowego bezpieczeństwa typów. PEP 698
Przykłady użycia @override
można zobaczyć tutaj.
Ulepszenia w wiadomościach błędów
Ulepszenia w wiadomościach błędów w Pythonie 3.12 mają na celu ułatwienie programistom identyfikacji i naprawy problemów poprzez dostarczanie bardziej szczegółowych i użytecznych informacji. Oto niektóre z kluczowych zmian:
Inteligentniejsze sugestie dotyczące błędów składni: Python 3.12 stara się dostarczać bardziej trafne sugestie dotyczące możliwych rozwiązań napotkanych błędów składni, co może pomóc w szybszym debugowaniu i mniej frustrującym doświadczeniu programowania.
Poprawa wiadomości o błędach związanych z typami: Zmiany w wiadomościach o błędach mają na celu lepsze wyjaśnienie problemów związanych z typami, co jest szczególnie przydatne w kontekście rozwijającego się wsparcia dla typowania statycznego w Pythonie.
Zwiększona przejrzystość błędów importowania: Ulepszenia w obsłudze i raportowaniu błędów importowania mają na celu ułatwienie identyfikacji brakujących lub nieprawidłowo zaimplementowanych importów, co jest częstym źródłem błędów, szczególnie w większych projektach.
Te ulepszenia mają na celu nie tylko uproszczenie procesu debugowania, ale także uczynienie Pythona bardziej przyjaznym dla użytkownika, co jest szczególnie ważne w kontekście rosnącej popularności języka wśród początkujących programistów i osób uczących się programowania.
Źródła informacji o tych zmianach mogą obejmować oficjalne notatki wydania Pythona 3.12, gdzie znajdują się szczegółowe opisy wszystkich zmian wprowadzonych w tej wersji. Ponadto PEP 673 - Self Type wprowadza Self
jako sposób na ulepszenie typowania, co może mieć wpływ na komunikaty o błędach związanych z typami. Dla pełniejszego obrazu zmian warto również śledzić dyskusje w społeczności Pythona oraz inne dokumenty PEP związane z wersją 3.12.
Nowości w typach generycznych i adnotacjach typów
Python 3.12 wprowadza szereg ulepszeń w obszarze typów generycznych i adnotacji typów, mających na celu zwiększenie elastyczności i czytelności kodu. Oto niektóre z kluczowych zmian, wraz z przykładami:
Uproszczona składnia dla typów generycznych: W Pythonie 3.12 składnia definicji typów generycznych została uproszczona, co ułatwia pracę z nimi. Przykład użycia nowej składni:
from typing import Generic, TypeVar T = TypeVar('T') class Box(Generic[T]): def __init__(self, item: T): self.item = item def get_item(self) -> T: return self.item
W poprzednich wersjach Pythona, tworzenie typów generycznych wymagało użycia bardziej rozbudowanej składni
PEP 709: Optymalizacja składni wyrażeń
PEP 709 wprowadza ulepszenia w składni wyrażeń w Pythonie, mające na celu zwiększenie wydajności i czytelności kodu. Wyrażenia to potężne narzędzie w Pythonie, umożliwiające szybkie i wyraźne tworzenie nowych list, słowników, zbiorów i innych struktur danych przez iterowanie i przetwarzanie elementów innej sekwencji lub iterowalnego obiektu. Optymalizacja zaproponowana w PEP 709 polega na zmniejszeniu narzutu związanego z tworzeniem funkcji anonimowych w trakcie wykonywania wyrażeń, co przekłada się na lepszą wydajność.
Przykłady
Przed PEP 709, wyrażenie listy (list comprehension), tak jak poniżej:
squares = [x**2 for x in range(10)]
Była transformowana wewnętrznie w coś podobnego do:
def temp():
result = []
for x in range(10):
result.append(x**2)
return result
squares = temp()
Taki sposób transformacji wymaga tworzenia dodatkowej funkcji, co wiąże się z pewnym narzutem wydajnościowym.
Po wprowadzeniu optymalizacji w PEP 709 Python może przetwarzać wyrażenia w bardziej wydajny sposób, eliminując konieczność tworzenia funkcji anonimowych i bezpośrednio wstawiając kod wyrażenia do bieżącej funkcji. To prowadzi do szybszego wykonania kodu, szczególnie w przypadkach intensywnego użycia wyrażenia.
Profiler Linux Perf
Linux Perf to zaawansowane narzędzie do profilowania dostępne na systemach Linux, które umożliwia szczegółową analizę wydajności aplikacji, w tym Pythona. Umożliwia ono zbieranie szerokiego zakresu danych o wydajności, takich jak liczniki sprzętowe, zdarzenia jądra i śledzenie wykonania programu. Wprowadzenie wsparcia dla Profilera Linux Perf w Pythonie 3.12 znacznie ułatwia analizę wydajności aplikacji Pythonowych, umożliwiając programistom głębszy wgląd w działanie ich kodu na poziomie systemu operacyjnego.
Przykłady
Aby użyć Profilera Linux Perf do analizy aplikacji Python, wystarczy prosta komenda:
perf record -g python my_script.py
Po zakończeniu działania skryptu, perf record
zapisze dane profilowania do pliku, który możesz następnie przeanalizować przy użyciu perf report
:
perf report
To polecenie otworzy interaktywny widok, w którym możesz przeglądać najbardziej czasochłonne funkcje i ścieżki wykonywania w twoim kodzie.
Dodatkowo możesz użyć perf annotate
, aby uzyskać bardziej szczegółowe informacje na temat konkretnych funkcji:
perf annotate -s my_function
Żródło: https://docs.python.org/3/howto/perf_profiling.html#perf-profiling
Podsumowanie
Python 3.12, z jego nowymi funkcjami i ulepszeniami, wnosi wiele ekscytujących zmian, które sprawiają, że programowanie w tym języku staje się jeszcze bardziej intuicyjne i wydajne. Od uproszczonej składni typów generycznych, przez rozszerzone możliwości f-stringów, aż po innowacje takie jak GIL na interpreter i nieśmiertelne obiekty, każda z tych zmian ma potencjał, aby znacząco wpłynąć na sposób, w jaki tworzymy i optymalizujemy nasz kod.
Dekorator @override
wprowadza dodatkowe bezpieczeństwo typów, umożliwiając wyraźne oznaczanie nadpisanych metod, co jest szczególnie przydatne w dużych projektach i złożonych hierarchiach dziedziczenia. Z kolei ulepszenia w wiadomościach o błędach mają na celu uczynienie procesu debugowania bardziej przejrzystym i mniej frustrującym, co jest kluczowe dla szybkiego rozwijania niezawodnych aplikacji.
W kontekście narzędzi diagnostycznych integracja z Profilerem Linux Perf otwiera nowe możliwości dla programistów Pythona, umożliwiając głębszą analizę wydajności aplikacji i identyfikację wąskich gardeł. Dzięki temu nawet najbardziej wymagające aplikacje mogą być teraz optymalizowane z niezrównaną precyzją.
Na zakończenie warto podkreślić, że te wszystkie zmiany i ulepszenia, choć techniczne, mają jeden wspólny cel: uczynić Pythona jeszcze bardziej przyjaznym, wydajnym i bezpiecznym językiem dla programistów na każdym poziomie zaawansowania. Wraz z rosnącą społecznością i ciągłym rozwojem, Python umacnia swoją pozycję jako jeden z najważniejszych języków w świecie technologii, a aktualizacje takie jak te wprowadzone w wersji 3.12 tylko to potwierdzają.
Dla tych, którzy chcą zgłębić temat, zachęcam do odwiedzenia oficjalnej strony Python Enhancement Proposals (PEP) oraz dokumentacji wydania Pythona 3.12, gdzie znajdziecie pełną dokumentację wprowadzonych zmian.
Zapraszam do zadawania pytań przez formularz kontaktowy. Pamiętaj, że jeśli potrzebujesz wsparcia, możesz napisać do mnie - pomogę.
Spodobał Ci się post?
Podziel się nim!
Masz uwagi do posta, chcesz porozmawiać, szukasz pomocy z Pythonem i Django? Napisz do mnie!