1
Current Location:
>
Python New Features
Python 3.9 to 3.11: Explosion of New Features, Are You Ready?
2024-11-10 03:07:02   read:12

Hey, Python enthusiasts! Today let's talk about the new features in Python versions 3.9 to 3.11. These versions have seen an explosion of new features, bringing many exciting improvements. Are you as eager as I am to try out these new functionalities? Let's take a look at how cool these new features are and what conveniences they can bring to our programming!

Dictionary Merging

Python 3.9 introduced the dictionary merge operators | and |=, which is great news! Remember how cumbersome it was to merge dictionaries before?

dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
merged = {**dict1, **dict2}


merged = dict1 | dict2

When I first saw this new syntax, my reaction was: "Wow, this is so convenient!" Not only is the code more concise, but the semantics are also clearer. Don't you think the | symbol looks like it's "joining" two dictionaries together?

The |= operator can directly update a dictionary:

dict1 |= dict2  # dict1 now contains all key-value pairs from dict2

This feature is particularly useful when dealing with configuration files. For example, you can define a default configuration and then update it with user-defined configurations:

default_config = {'debug': False, 'log_level': 'INFO'}
user_config = {'debug': True}
final_config = default_config | user_config

Don't you feel the code has become more elegant?

Decorator Syntax

Speaking of improvements in Python 3.9, we can't ignore the relaxation of decorator syntax. Previously, decorator expressions had to be simple names, like @decorator. But now, we can use more complex expressions:

@decorator1.decorator2
def func():
    pass

@decorator(arg1, arg2)
def func():
    pass

This improvement may seem small, but it actually gives us more flexibility. You can more easily combine multiple decorators or pass parameters to decorators. I personally find this feature particularly useful when building complex decorator systems, such as defining routes and middleware in web frameworks.

Type Hints

Python 3.9 has also made significant progress in type hints. Now, we can directly use built-in collection types as type hints without needing to import from the typing module:

from typing import List, Dict, Tuple

def process_data(data: List[int]) -> Dict[str, Tuple[int, float]]:
    pass


def process_data(data: list[int]) -> dict[str, tuple[int, float]]:
    pass

This change may seem trivial, but it does make the code more concise. Moreover, I think this way of writing is more in line with Python's philosophy - explicit is better than implicit. You can immediately understand the meaning of list[int] without having to guess where List is imported from.

String Processing

Python 3.9 also added two new methods to the string type: removeprefix() and removesuffix(). These two methods make handling string prefixes and suffixes more convenient:

text = "Hello, World!"
print(text.removeprefix("Hello, "))  # Output: World!
print(text.removesuffix("!"))  # Output: Hello, World

You might say, couldn't this be achieved with slicing? True, but these two methods provide a safer and clearer way. For example, if the prefix or suffix doesn't exist, these methods will safely return the original string without throwing an exception.

I find this feature particularly useful when dealing with filenames or URLs. Imagine you want to remove a common prefix from a batch of filenames:

filenames = ["prefix_file1.txt", "prefix_file2.txt", "prefix_file3.txt"]
cleaned = [name.removeprefix("prefix_") for name in filenames]

Isn't this more intuitive and safer than using slicing or string replacement?

Structural Pattern Matching

When it comes to Python 3.10, we can't ignore structural pattern matching. This feature can be said to be one of the most important syntax improvements in Python in recent years. It introduces the match-case statement, allowing us to pattern match based on the structure of data:

def process_command(command):
    match command.split():
        case ["quit"]:
            print("Goodbye!")
            exit()
        case ["greet", name]:
            print(f"Hello, {name}!")
        case ["add", x, y]:
            print(f"The sum is: {int(x) + int(y)}")
        case _:
            print("Unknown command")

process_command("greet Alice")  # Output: Hello, Alice!
process_command("add 5 3")  # Output: The sum is: 8

Seeing this example, don't you feel the code has become clearer and more elegant? When I first saw this feature, I was amazed! It allows us to handle complex data structures and conditional logic in a very intuitive way.

This feature is particularly useful in scenarios such as handling JSON data, parsing command-line arguments, implementing state machines, etc. For example, you can easily parse complex JSON structures:

def process_json(data):
    match data:
        case {"type": "user", "name": str(name), "age": int(age)}:
            print(f"User: {name}, Age: {age}")
        case {"type": "product", "name": str(name), "price": float(price)}:
            print(f"Product: {name}, Price: ${price:.2f}")
        case _:
            print("Unknown data structure")

process_json({"type": "user", "name": "Alice", "age": 30})

Don't you think this way of writing is easier to understand and maintain than a bunch of nested if-else statements?

Stricter zip

Python 3.10 also improved the zip function. Now, if you don't specify strict=False, zip will check if all input iterables have the same length, and if not, it will throw an exception:

list1 = [1, 2, 3]
list2 = ['a', 'b']


try:
    result = list(zip(list1, list2))
except ValueError as e:
    print(f"Error: {e}")


result = list(zip(list1, list2, strict=False))
print(result)  # Output: [(1, 'a'), (2, 'b')]

This change might seem troublesome at first glance, but I think it actually helps us avoid some potential errors. Imagine if you're dealing with two lists that should be of equal length, but for some reason they're not, the old version of zip would quietly ignore the extra elements. The new version will immediately tell you there's a problem, allowing you to discover and fix errors in a timely manner.

Performance Optimization

When it comes to Python 3.11, the most exciting thing is performance optimization. Officials claim that Python 3.11 is on average 25% faster than 3.10, and in some cases can be up to 60% faster. This improvement is quite remarkable!

Although we may not be able to directly feel this performance improvement, it will indeed make our programs run faster. Especially for those computationally intensive tasks, this improvement could have a significant impact.

Personally, I think this performance optimization is particularly important for Python's applications in fields such as scientific computing and machine learning. It makes Python more competitive in terms of performance while maintaining its ease of use.

Error Handling Improvements

Python 3.11 also has significant improvements in error handling. The new version provides more precise error location information, even pointing out the specific expression that caused the error:

def divide(a, b):
    return a / b

result = divide(10, 0)

In Python 3.10, you might see an error message like this:

Traceback (most recent call last):
  File "example.py", line 4, in <module>
    result = divide(10, 0)
  File "example.py", line 2, in divide
    return a / b
ZeroDivisionError: division by zero

While in Python 3.11, the error message will be more detailed:

Traceback (most recent call last):
  File "example.py", line 4, in <module>
    result = divide(10, 0)
             ^^^^^^^^^^^^^^
  File "example.py", line 2, in divide
    return a / b
           ~~^~~
ZeroDivisionError: division by zero

Seeing this improvement, don't you feel debugging code has become easier? When I first saw such detailed error messages, I wanted to applaud the Python development team!

Additionally, Python 3.11 introduced exception groups and except* syntax, allowing us to handle multiple exceptions more flexibly:

try:
    # Code that might throw multiple exceptions
except* TypeError as e:
    print(f"Caught TypeError: {e.exceptions}")
except* ValueError as e:
    print(f"Caught ValueError: {e.exceptions}")

This feature is particularly useful when dealing with complex exception situations. You can more precisely catch and handle different types of exceptions, rather than using a large except block to handle all exceptions indiscriminately.

New Modules

Python 3.9 and 3.11 introduced the zoneinfo and tomllib modules respectively. The zoneinfo module provides access to the IANA time zone database, making it easier to handle different time zones:

from zoneinfo import ZoneInfo
from datetime import datetime


dt = datetime(2023, 6, 1, 12, 0, tzinfo=ZoneInfo("America/New_York"))
print(dt)  # Output: 2023-06-01 12:00:00-04:00

The tomllib module provides built-in support for TOML parsing:

import tomllib

toml_str = """
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00

[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
"""

data = tomllib.loads(toml_str)
print(data['owner']['name'])  # Output: Tom Preston-Werner

Both of these modules fill some gaps in Python's standard library. zoneinfo makes it easier for us to handle time zone issues, while tomllib provides native support for the increasingly popular TOML configuration format. Don't you also feel that these new modules make Python more comprehensive and powerful?

Summary

Looking back at the new features from Python 3.9 to 3.11, we can see that Python is constantly evolving, becoming more powerful, efficient, and user-friendly. From syntax improvements to performance optimizations, from new modules to better error handling, every change is aimed at enhancing our programming experience.

These new features not only make our code more concise and elegant but also help us solve problems more efficiently. However, to fully utilize these new features, we also need to continuously learn and practice.

Which new feature do you like the most? How has it changed your programming style? Feel free to share your thoughts and experiences in the comments!

Remember, the world of Python is always full of surprises. Keep your curiosity and keep learning, you'll find that the joy of programming is endless. Let's embrace these new features together and write better Python code!

>Related articles