-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #587 from realpython/python-313-examples
Add examples from Python 3.13: Cool New Features
- Loading branch information
Showing
18 changed files
with
333 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ Note that for testing the free-threading and JIT features, you'll need to build | |
|
||
You can learn more about Python 3.13's new features in the following Real Python tutorials: | ||
|
||
<!-- - [Python 3.13: Cool New Features for You to Try](https://realpython.com/python313-new-features/) --> | ||
- [Python 3.13: Cool New Features for You to Try](https://realpython.com/python313-new-features/) | ||
- [Python 3.13 Preview: Free Threading and a JIT Compiler](https://realpython.com/python313-free-threading-jit/) | ||
- [Python 3.13 Preview: A Modern REPL](https://realpython.com/python313-repl) | ||
|
||
|
@@ -30,11 +30,28 @@ The following examples are used to demonstrate different features of the new REP | |
- [`multiline_editing.py`](repl/multiline_editing.py) | ||
- [`power_factory.py](repl/power_factory.py) | ||
- [`guessing_game.py](repl/guessing_game.py) | ||
- [`roll_dice.py`](repl/roll_dice.py) | ||
|
||
### Error messages | ||
|
||
Run the scripts in the `errors/` folder to see different error messages produced by Python 3.13. | ||
|
||
### Free-Threading and JIT | ||
|
||
You need to enable a few build options to try out the free-threading and JIT features in Python 3.13. You can find more information in the dedicated [README file](free-threading-jit/README.md). | ||
|
||
## Static typing | ||
|
||
Run the scripts in the `typing/` folder to try out the new static typing features. | ||
|
||
## Other features | ||
|
||
The following scripts illustrate other new features in Python 3.13: | ||
|
||
- [`replace.py`](replace.py): Use `copy.replace()` to update immutable data structures. | ||
- [`paths.py`](paths.py) and [`music/`](music/): Glob patterns are more consistent. | ||
- [`docstrings.py`](docstrings.py): Common leading whitespace in docstrings is stripped. | ||
|
||
## Authors | ||
|
||
- **Bartosz Zaczyński**, E-mail: [[email protected]]([email protected]) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import dataclasses | ||
|
||
|
||
@dataclasses.dataclass | ||
class Person: | ||
"""Model a person with a name, location, and Python version.""" | ||
|
||
name: str | ||
place: str | ||
version: str | ||
|
||
|
||
print(Person.__doc__) | ||
|
||
print(len(dataclasses.replace.__doc__)) | ||
print(dataclasses.replace.__doc__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
def inverse(number): | ||
return 1 / number | ||
|
||
|
||
print(inverse(0)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
numbers = [2, 0, 2, 4, 1, 0, 0, 1] | ||
|
||
# print(sorted(numbers, reversed=True)) | ||
print(sorted(numbers, reverse=True)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import random | ||
|
||
num_faces = 6 | ||
|
||
print("Hit enter to roll die (q to quit, number for # of faces) ") | ||
while True: | ||
roll = input() | ||
if roll.lower().startswith("q"): | ||
break | ||
if roll.isnumeric(): | ||
num_faces = int(roll) | ||
|
||
result = random.randint(1, num_faces) | ||
print(f"Rolling a d{num_faces:<2d} - {result:2d}") |
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import glob | ||
import re | ||
from pathlib import Path | ||
|
||
print('\nUsing glob("*"):\n') | ||
for path in Path("music").glob("*"): | ||
print(" ", path) | ||
|
||
print('\nUsing glob("**"):\n') | ||
for path in Path("music").glob("**"): | ||
print(" ", path) | ||
|
||
print('\nUsing glob("**/*"):\n') | ||
for path in Path("music").glob("**/*"): | ||
print(" ", path) | ||
|
||
print('\nUsing glob("**/"):\n') | ||
for path in Path("music").glob("**/"): | ||
print(" ", path) | ||
|
||
print("\nglob.translate()\n") | ||
pattern = glob.translate("music/**/*.txt") | ||
print(pattern) | ||
|
||
print(re.match(pattern, "music/opera/flower_duet.txt")) | ||
print(re.match(pattern, "music/progressive_rock/")) | ||
print(re.match(pattern, "music/progressive_rock/fandango.txt")) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import random | ||
|
||
num_faces = 6 | ||
|
||
print("Hit enter to roll die (q to quit, number for # of faces) ") | ||
while True: | ||
roll = input() | ||
if roll.lower().startswith("q"): | ||
break | ||
if roll.isnumeric(): | ||
num_faces = int(roll) | ||
|
||
result = random.randint(1, num_faces) | ||
print(f"Rolling a d{num_faces:<2d} - {result:2d}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import copy | ||
from datetime import date | ||
from typing import NamedTuple | ||
|
||
|
||
class Person(NamedTuple): | ||
name: str | ||
place: str | ||
version: str | ||
|
||
|
||
person = Person(name="Geir Arne", place="Oslo", version="3.12") | ||
person = Person(name=person.name, place=person.place, version="3.13") | ||
print(person) | ||
|
||
today = date.today() | ||
print(today) | ||
print(today.replace(day=1)) | ||
print(today.replace(month=12, day=24)) | ||
|
||
person = Person(name="Geir Arne", place="Oslo", version="3.12") | ||
print(copy.replace(person, version="3.13")) | ||
print(copy.replace(today, day=1)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
"""Demonstration of PEP 702: Marking deprecations using the type system | ||
The deprecations should be marked in PyCharm and VS Code. | ||
Use PyLance in VS Code by setting Python › Analysis: Type Checking Mode or run | ||
the Pyright CLI: | ||
$ python -m pip install pyright | ||
$ pyright --pythonversion 3.13 . | ||
Note that showing warnings with Pyright requires setting the reportDeprecated | ||
option. This is done in the accompanying pyproject.toml. | ||
""" | ||
|
||
from typing import overload | ||
from warnings import deprecated | ||
|
||
|
||
@deprecated("Use + instead of calling concatenate()") | ||
def concatenate(first: str, second: str) -> str: | ||
return first + second | ||
|
||
|
||
@overload | ||
@deprecated("add() is only supported for floats") | ||
def add(x: int, y: int) -> int: ... | ||
@overload | ||
def add(x: float, y: float) -> float: ... | ||
|
||
|
||
def add(x, y): | ||
return x + y | ||
|
||
|
||
class Version: | ||
def __init__(self, major: int, minor: int = 0, patch: int = 0) -> None: | ||
self.major = major | ||
self.minor = minor | ||
self.patch = patch | ||
|
||
@property | ||
@deprecated("Use .patch instead") | ||
def bugfix(self): | ||
return self.patch | ||
|
||
def bump(self, part: str) -> None: | ||
if part == "major": | ||
self.major += 1 | ||
self.minor = 0 | ||
self.patch = 0 | ||
elif part == "minor": | ||
self.minor += 1 | ||
self.patch = 0 | ||
elif part == "patch": | ||
self.patch += 1 | ||
else: | ||
raise ValueError("part must be 'major', 'minor', or 'patch'") | ||
|
||
@deprecated("Use .bump() instead") | ||
def increase(self, part: str) -> None: | ||
return self.bump(part) | ||
|
||
def __str__(self): | ||
return f"{self.major}.{self.minor}.{self.patch}" | ||
|
||
|
||
@deprecated("Use Version instead") | ||
class VersionType: | ||
def __init__(self, major: int, minor: int = 0, patch: int = 0) -> None: | ||
self.major = major | ||
self.minor = minor | ||
self.patch = patch | ||
|
||
|
||
concatenate("three", "thirteen") | ||
add(3, 13) | ||
VersionType(3, 13) | ||
|
||
version = Version(3, 13) | ||
version.increase("patch") | ||
print(version) | ||
print(version.bugfix) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from collections import deque | ||
|
||
|
||
class Queue[T]: | ||
def __init__(self) -> None: | ||
self.elements: deque[T] = deque() | ||
|
||
def push(self, element: T) -> None: | ||
self.elements.append(element) | ||
|
||
def pop(self) -> T: | ||
return self.elements.popleft() | ||
|
||
|
||
# %% Python 3.13 | ||
# | ||
# class Queue[T=str]: | ||
# def __init__(self) -> None: | ||
# self.elements: deque[T] = deque() | ||
# | ||
# def push(self, element: T) -> None: | ||
# self.elements.append(element) | ||
# | ||
# def pop(self) -> T: | ||
# return self.elements.popleft() | ||
|
||
# %% Use the queue | ||
# | ||
string_queue = Queue() | ||
integer_queue = Queue[int]() | ||
|
||
string_queue.push("three") | ||
string_queue.push("thirteen") | ||
print(string_queue.elements) | ||
|
||
integer_queue.push(3) | ||
integer_queue.push(13) | ||
print(integer_queue.elements) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[tool.pyright] | ||
reportDeprecated = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
"""Demonstration of PEP 705: TypedDict: read-only items | ||
Use PyLance in VS Code by setting Python › Analysis: Type Checking Mode or run | ||
the Pyright CLI: | ||
$ python -m pip install pyright $ pyright --pythonversion 3.13 . | ||
Extension of TypedDict: | ||
https://realpython.com/python38-new-features/#more-precise-types | ||
""" | ||
|
||
from typing import NotRequired, ReadOnly, TypedDict | ||
|
||
# %% Without ReadOnly | ||
|
||
# class Version(TypedDict): | ||
# version: str | ||
# release_year: NotRequired[int | None] | ||
|
||
|
||
# class PythonVersion(TypedDict): | ||
# version: str | ||
# release_year: int | ||
|
||
|
||
# %% Using ReadOnly | ||
# | ||
# Can only use PythonVersion as a Version if the differing fields are ReadOnly | ||
class Version(TypedDict): | ||
version: str | ||
release_year: ReadOnly[NotRequired[int | None]] | ||
|
||
# Note that ReadOnly can be nested with other special forms in any order | ||
# release_year: NotRequired[ReadOnly[int | None]] | ||
|
||
|
||
class PythonVersion(TypedDict): | ||
version: str | ||
release_year: ReadOnly[int] | ||
|
||
|
||
# %% Work with Version and PythonVersion | ||
# | ||
def get_version_info(ver: Version) -> str: | ||
if "release_year" in ver: | ||
return f"Version {ver['version']} released in {ver['release_year']}" | ||
else: | ||
return f"Version {ver['version']}" | ||
|
||
|
||
py313 = PythonVersion(version="3.13", release_year=2024) | ||
|
||
# Alternative syntax, using TypedDict as an annotation | ||
# py313: PythonVersion = {"version": "3.13", "release_year": 2024} | ||
|
||
print(get_version_info(py313)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
from typing import TypeGuard | ||
|
||
type Tree = list[Tree | int] | ||
|
||
|
||
def is_tree(obj: object) -> TypeGuard[Tree]: | ||
return isinstance(obj, list) | ||
|
||
|
||
def get_left_leaf_value(tree_or_leaf: Tree | int) -> int: | ||
if is_tree(tree_or_leaf): | ||
return get_left_leaf_value(tree_or_leaf[0]) | ||
else: | ||
return tree_or_leaf | ||
|
||
|
||
# %% Python 3.13 | ||
# | ||
# from typing import TypeIs | ||
# | ||
# type Tree = list[Tree | int] | ||
# | ||
# def is_tree(obj: object) -> TypeIs[Tree]: | ||
# return isinstance(obj, list) | ||
# | ||
# def get_left_leaf_value(tree_or_leaf: Tree | int) -> int: | ||
# if is_tree(tree_or_leaf): | ||
# return get_left_leaf_value(tree_or_leaf[0]) | ||
# else: | ||
# return tree_or_leaf | ||
|
||
# %% Use the tree | ||
# | ||
print(get_left_leaf_value([[[[3, 13], 12], 11], 10])) |