Initial experience of new features of Python 3.10

Initial experience of new features of Python 3.10


Note: picture source

catalogue

  • Structural pattern matching [PEP 635]
  • union type allows X | Y [PEP 604]
  • Parenthesized context manager

1, Structure pattern matching (New) PEP 635)

If you are familiar with other languages, you must know that the switch - > case statement has never had this syntax before Python 3.10, but in this update, version 3.10 + supports this syntax for structural pattern matching - match - > case

1. Basic grammar

match expression: # Condition, which can be any expression or variable
    case pattern_1: # Pattern, condition to match 1
        ... # Code block to execute
    case pattern_2: # Pattern, condition to match 2
        ... # Code block to execute
    case _: # Mode, indicating default
        ... # Code block to execute

2. Pattern

1. as mode (AS)

def simplify_expr(tokens):
    match tokens:
        case [('(' | '[') as l, *expr, (')' | ']') as r] if (l + r) in ('()', '[]'):
            # The first parameter must be ('(' | '[') and the alias is l
            # The last parameter must be ('(' | '[') and the alias is r
            # And you need to judge that (l + r) in ('()', '[]') is True before entering this code block for execution
            print(expr)
        case [0, ('+' | '-') as op, right]:
            # The first parameter must be 0
            # The second parameter must be ('+' | '-'), alias op
            # The third parameter is any number
            # The length of the sequence must be 3
            print(op, right)
        case [(int() | float()) as value]:
            # The length of a sequence can only be 1
            # Moreover, the type can only be int or float
            print(value)
        case _:
            # If it doesn't match, go here
            print("Not matched to")


simplify_expr(['(', "aaa", "bbb", ")"])  # ['aaa', 'bbb']
simplify_expr(['[', "aaa", "bbb", "]"])  # ['aaa', 'bbb']
simplify_expr(['[', "aaa", "bbb", ")"])  # Not matched to
simplify_expr(['[', "]"])  # []
simplify_expr(['bbb', '[', "]"])  # Not matched to
simplify_expr([0, "+", 3])  # + 3
simplify_expr([0, "-", 3])  # - 3
simplify_expr([0, "-", 3, 34])  # Not matched to
simplify_expr([1])  # 1
simplify_expr([1.0])  # 1.0
simplify_expr([1.0, 1])  # Not matched to
simplify_expr(["aaaa"])  # Not matched to

2. or mode (|)

def demo(code):
    match code:
        case 200:
            print("200 succeed")
        case 400 | 404:
            print("Something's wrong")
        case (1002 | 1006, 200): # Organize two values (organization mode)
            print("Interface request succeeded")
        case _:
            print("Cannot match")


demo(200) # 200 succeeded
demo(400) # Something's wrong
demo(404) # Something's wrong
demo((1006, 200)) # Interface request succeeded
demo(502) # Cannot match

3. Text mode (Literal)

def simplify(expr):
    match expr:
        case ('+', 0, x):
            return x
        case ('+' | '-', x, 0):
            return x
        case ('and', True, x):
            return x
        case ('and', False, x):
            return False
        case ('or', False, x):
            return x
        case ('or', True, x):
            return True
        case ('not', ('not', x)):
            return x
    return expr


print(simplify(('+', 0, 12)))  # 12
print(simplify(('+', 13, 0)))  # 13
print(simplify(('-', 13, 0)))  # 13
print(simplify(('and', True, 0)))  # 0
print(simplify(('and', False, 0)))  # False
print(simplify(('or', False, 1)))  # 1
print(simplify(('or', True, 1)))  # True
print(simplify(('not', ('not', 23))))  # 23

4. Capture mode (Capture)

def average(*args):
    match args:
        case [x, y]:
            return (x + y) / 2
        case [x]:
            return x
        case []:
            return 0
        case [x, y, z]:
            return x + y + z
        case a:
            return sum(a) / len(a)


print(average(1, 2, 3, 4)) # 2.5 ==> a ==> sum(a) / len(a)==> (1+2+3+4)/4 = 2.5
print(average(1, 2))  # 1.5 ==> [x, y] ==> (x+y)/2 ==> (1+2)/2=1.5
print(average(1))  # 1 ==> [x] ==> x=1
print(average())  # 0 ==> [] ==> 0
# If three elements are passed, they will be matched according to the length, which is different from the dictionary type
print(average(1, 2, 3))  # 6 ==> [x, y, z] ==> 1+2+3=6

5. Wildcard pattern (Wildcard)

  • _: any character preceded by:
  • case: here is at the end, indicating default
def is_closed(sequence):
    match sequence:
        case [_]:  # A sequence of only one element
            return True
        case [start, *_, end]:  # A sequence containing at least two elements
            return start == end
        case _:  # Match any type
            return False


print(is_closed(1))  # False ==> _ ==> False
print(is_closed([1]))  # True ==> [_] ==> True
print(is_closed([1, 2, 3]))  # False ==> [start, *_, end] ==>>1 ==3 ==>False

6. Value mode (value)

class HttpStatus(object):
    OK = 200
    MOVED_PERMANENTLY = 301
    NOT_FOUND = 404


class MimeType(object):
    TEXT = "text"
    APPL_ZIP = "appl_zip"


def handle_reply(reply):
    match reply:
        case (HttpStatus.OK, MimeType.TEXT, body):
            print(f"Request succeeded, type is TEXT,body==>{body}")
        case (HttpStatus.OK, MimeType.APPL_ZIP, body):
            print(f"Request succeeded, type is APPL_ZIP,body==>{body}")
        case (HttpStatus.MOVED_PERMANENTLY, new_URI):
            print(f"301 OK, check it quickly URI==>{new_URI}")
        case (HttpStatus.NOT_FOUND):
            print("Website hung up...")


handle_reply([200, "text", "dddddd"])  # The request is successful. The type is text, body = = > dddd
handle_reply([200, "appl_zip", "dddddd"])  # The request is successful. The type is appl_zip, body = = > dddd
handle_reply([200, "appl_zip", "dddddd"])  # The request is successful. The type is appl_zip, body = = > dddd
handle_reply([301, "sip:smith@zte.com.cn"]) # 301, check the URI = = > SIP: smith@zte.com.cn
handle_reply(404)  # Website hung up...

7. Organizational model (Group)

Implementation using or mode

def demo(code):
    match code:
        case 200:
            print("200 succeed")
        case 400 | 404:
            print("Something's wrong")
        case (1002 | 1006, 200): # Organize two values (organization mode)
            print("Interface request succeeded")
        case _:
            print("Cannot match")

demo((1006, 200)) # Interface request succeeded

8. Sequence

def is_closed(sequence):
    match sequence:
        case [_]:  # A sequence of only one element
            return True
        case [start, *_, end]:  # A sequence containing at least two elements
            return start == end
        case _:  # Match any type
            return False


print(is_closed(1))  # False ==> _ ==> False
print(is_closed([1]))  # True ==> [_] ==> True
print(is_closed([1, 2, 3]))  # False ==> [start, *_, end] ==>>1 ==3 ==>False

9. Mapping mode

def change_red_to_blue(json_obj):
    match json_obj:
        case {'color': ('red' | '#FF0000')}:
            json_obj['color'] = 'blue'
        case {'children': children}:
            for child in children:
                change_red_to_blue(child)


color = {"color": "red"}
print(color)  # {'color': 'red'}
change_red_to_blue(color)
print(color)  # {'color': 'blue'}

# Note: if the transfer parameter is a dictionary, match it from the bottom. As long as it is matched, it will not be matched from the bottom
# As follows, color contains two keys: color and children. When matching, the matching ends when color is matched, and the next step of matching children will not be carried out
color = {"color": "red", "children": [{"color": "#FF0000"}]}
print(color)  # {'color': 'red', 'children': [{'color': '#FF0000'}]}
change_red_to_blue(color)
print(color)  # {'color': 'blue', 'children': [{'color': '#The color in FF0000'}]} children will not be modified

10. Class mode (Class)

class MyClassA(object):
    def __init__(self, name) -> None:
        self.name = name


def demo(class_):

    match class_:
        case MyClassA(name="desire"):
            print("yes desire ah")
        case _:
            print(class_.name)


demo(MyClassA("ronin")) # ronin ==> _ ==> MyClassA("ronin").name ==> class_.name=ronin
demo(MyClassA("desire")) # Yes, yes = = > myclassa (name = "yes") = = > Print ("yes, yes")

3. Match the order to be noted

  • When the match object is a list or tuple, it needs to match the length and element value to hit. It is reflected in the capture mode.
  • When the match object is a dict, the rules are different. As long as the key in the case expression exists in the match object, it can be hit, which is reflected in the mapping mode.
  • When the match object is a class object, the matching rule is a bit similar to dict. As long as the object type and object attributes meet the case conditions, they can be hit, which is reflected in the class pattern.

2, The union type allows X | Y( PEP 604)

The new syntax union accepts function, variable, and parameter annotations.

1. Simple grammar

# before
from typing import List, Union, Optional


def f(list: List[Union[int, str]], param: Optional[int]) -> Union[float, str]:
    pass

# now is more concise
from typing import List


def f(list: List[int | str], param: int | None) -> float | str:
    pass

2. typing.Union and | are equivalent

int | str == typing.Union[int, str] # True

3. The order is not required

(int | str) == (str | int) # True
(int | str | float) == typing.Union[str, float, int] # True

4. isinstance and issubclass support

isinstance(5, int | str)  # True
isinstance("aaa", int | str)  # True
isinstance(1.22, int | str)  # False
issubclass(bool, int | float)  # True
isinstance(None, int | None)  # True
isinstance(42, None | int)  # True

3, Parenthesized context manager

The use of outer parentheses has been supported to enable multiple context managers to write multiple lines in succession. This allows an overly long set of context managers to be formatted as multiple lines in a similar manner to previous import statements.
The following four formats are supported

# Previous formats do not affect usage
with open("readme.md", 'r') as f:
    pass

with (open("readme.md", 'r') as f):
    pass

with (open("readme.md", 'r') as f1,
      open("readme.md", 'r') as f2):
    pass

with (open("readme.md", 'r'),
      open("readme.md", 'r') as f2):
    pass
with (open("readme.md", 'r'),
      open("readme.md", 'r'),
      ):
    pass

enclosure

References

Tags: Python

Posted on Tue, 12 Oct 2021 15:59:52 -0400 by nvidia