2.10. New Features in Python#

This section shows some new features in Python 3.10 and above.

2.10.1. Simplify Conditional Execution with Match Statements#

It is common to use the if-else statements to execute multiple conditional statements.

def get_price(food: str):
    if food == "apple" or food == "peach":
        return 4
    elif food == "orange":
        return 3
    elif food == "grape":
        return 5
    else:
        return "Unknown"


get_price("peach")
4

In Python 3.10 and above, you can use the match statements to do the same thing.

Match statements can provide a more concise and readable syntax, especially when dealing with complex pattern matching scenarios.

def get_price(food: str):
    match food:
        case "apple" | "peach":
            return 4
        case "orange":
            return 3
        case "grape":
            return 5
        case _:
            return "Unknown"


get_price("peach")
4

2.10.2. Structural Pattern Matching in Python 3.10#

Have you ever wanted to match complex data types and extract their information?

Python 3.10 allows you to do exactly that with the match statement and the case statements.

The code below uses structural pattern matching to extract ages from the matching data structure.

def get_youngest_pet(pet_info):
    match pet_info:
        case [{"age": age1}, {"age": age2}]:
            print("Age is extracted from a list")
            return min(age1, age2)

        case {'age': {}}:
            print("Age is extracted from a dict")
            ages = pet_info['age'].values()
            return min(ages)
pet_info1 = [{"name": "bim", "age": 1}, {"name": "pepper", "age": 9}]
get_youngest_pet(pet_info1)
Age is extracted from a list
1
pet_info2 = {'age': {"bim": 1, "pepper": 9}}
get_youngest_pet(pet_info2)
Age is extracted from a dict
1

2.10.3. Enhance Code Readability with Python Dataclasses and Match Statements#

You can use Python dataclasses with Python match statements to create cleaner and more readable code. This approach can be particularly useful when setting conditions based on multiple attributes of a class, as it can simplify the code and make it easier to understand.

Using if-else:

from dataclasses import dataclass

@dataclass
class SubscriptionPlan:
    name: str
    price: float
    unit: str

def get_plan_details(plan):
    if plan.name == "basic" and plan.unit == "month":
        return f"${plan.price} per month for one month."
    elif plan.name == "premium" and plan.unit == "year":
        return f"${plan.price} per year for one year."
    elif plan.name == "" and plan.price == 0.0 and plan.unit == "":
        return "Invalid subscription plan"
    else:
        return "Unknown subscription plan"
basic_plan = SubscriptionPlan(name="basic", price=9.99, unit="month")
premium_plan = SubscriptionPlan(name="premium", price=99.99, unit="year")

print(get_plan_details(basic_plan))
print(get_plan_details(premium_plan))
$9.99 per month for one month.
$99.99 per year for one year.

Using match statements:

from dataclasses import dataclass


@dataclass
class SubscriptionPlan:
    name: str
    price: float
    unit: str


def get_plan_details(plan):
    match plan:
        case SubscriptionPlan(name="basic", price=price, unit="month"):
            return f"${price} per month for one month."
        case SubscriptionPlan(name="premium", price=price, unit="year"):
            return f"${price} per year for one year."
        case SubscriptionPlan():
            return "Invalid subscription plan"
        case _:
            return "Unknown subscription plan"
basic_plan = SubscriptionPlan(name="basic", price=9.99, unit="month")
premium_plan = SubscriptionPlan(name="premium", price=99.99, unit="year")

print(get_plan_details(basic_plan))
print(get_plan_details(premium_plan))
$9.99 per month for one month.
$99.99 per year for one year.

2.10.4. Write Union Types as X|Y in Python 3.10#

Before Python 3.10, you need to use typing.Union to declare that a variable can have one of several different types.

from typing import Union

num = 2.3
isinstance(num, Union[int, float])
True

In Python 3.10, you can replace Union[X, Y] with X | Y to simplify the expression.

isinstance(num, int | float)
True

2.10.5. Walrus Operator: Assign a Variable in an Expression#

The walrus operator (:=) in Python 3.8 and above allows you to assign a variable in an expression. The walrus operator is useful when you want to:

  • Debug the components in an expression

  • Avoid repeated computations

  • Assign a meaningful name to an expression

from math import pi

diameter = 4
# without Walrus operator

circle = {
    "radius": diameter / 2, # computed twice
    "area": pi * (diameter / 2)**2,
}

diameter / 2
2.0
# with Walrus operator

circle = {
    "radius": (radius := diameter / 2),
    "area": pi * radius**2,
}

radius
2.0

2.10.6. Fine-Grained Traceback in Python 3.11#

Having a clear traceback makes it faster to debug your code. Python 3.11 provides fine-grained error locations in tracebacks, enabling developers to quickly identify the exact location of errors.

The following examples illustrate the difference in traceback between Python 3.9 and Python 3.11.

%%writefile trackback_test.py
def greet(name):
    greeting = "Hello, " + name + "!"
    print(greetng) # Error: Typo in variable name

greet("Khuyen")
# Python 3.9
$ python trackback_test.py
Traceback (most recent call last):
  File "/Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter1/trackback_test.py", line 5, in <module>
    greet("Khuyen")
  File "/Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter1/trackback_test.py", line 3, in greet
    print(greetng) # Error: Typo in variable name
NameError: name 'greetng' is not defined
# Python 3.11
$ python trackback_test.py
Traceback (most recent call last):
  File "/Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter1/trackback_test.py", line 5, in <module>
    greet("Khuyen")
  File "/Users/khuyentran/book/Efficient_Python_tricks_and_tools_for_data_scientists/Chapter1/trackback_test.py", line 3, in greet
    print(greetng) # Error: Typo in variable name
          ^^^^^^^
NameError: name 'greetng' is not defined. Did you mean: 'greeting'?