Focus on WeChat Public Number: K gor Crawler, QQ Exchange Group: 808574309, continue to share crawler advanced, JS/Android reverse technology dry goods!
Preface
The other day, October 4, Python released version 3.10.0. What? Not 4.0 after 3.9? (Manual Dog Head) In fact, Uncle Guido van Rossum, the father of Python, said as early as September last year:
- Version 3.10 after 3.9; In fact, it already exists (in the main branch of Github Master).
- If you have version 4, the transition from 3 to 4 is more like 1 to 2 than 2 to 3.
Compared to Python 3.9, the main new features of Python 3.10 are as follows:
PEP 634 - PEP 636: Structural pattern matching
In this many updates, Structural Pattern Matching structure pattern matching, match-case statement is undoubtedly the most exciting feature, similar to switch-case statements in other languages such as Java, C, Go, etc., for reference: PEP 636
Let's take a simple example:
def http_error(status): match status: case 400: print("Bad request") case 404: print("Not found") case 418: print("I'm a teapot") case _: print("Something's wrong with the internet") http_error(418) # I'm a teapot http_error(500) # Something's wrong with the internet
In the code above, the _in the last case Not as a variable name, but as a special pattern, if none of the previous cases have been hit, the case will be the last guarantee to ensure a hit, which is equivalent to the default branch in Java, C, Go and other languages:
public class HttpError { public static void main(String args[]){ int status = 500; switch(status){ case 400: System.out.println("Bad request"); case 404: System.out.println("Not found"); case 418: System.out.println("I'm a teapot"); default: System.out.println("Something's wrong with the internet"); } } } // Something's wrong with the internet
The match-case syntax supports variable parameters *args and **rest.
*The use of args and variable parameters in Python functions are one usage that allows multiple parameters to be passed in:
def create_user(command): match command.split(): case ["quit"]: quit() case ["create", user]: print("create", user) case ["create", *user]: for u in user: print("create", u) case _: print("command '{command}' not understood") create_user("create user1") create_user("create user2 user3 user4") # create user1 # create user2 # create user3 # create user4
**rest matches all key s and value s in the dictionary:
def get_dict(dic): match dic: case {**rest}: print("get dict:", rest) case _: print("parameter not understood") get_dict({"400": "Bad request", "404": "Not found", "418": "I'm a teapot"}) # get dict: {'400': 'Bad request', '404': 'Not found', '418': "I'm a teapot"}
It is important to note that structure pattern matching has different rules when it comes to different objects.
When the matching object is a list or tuple, both length and element value match to hit:
def create_user(param): match param: case ("quit"): quit() case ("create", user): print("create", user) case ("create", *user): for u in user: print("create", u) case _: print("command '{command}' not understood") create_user(("create", "user1", "user2")) # create user1 # create user2
When a matching object is a dictionary (dict), a key in a case expression can be hit as long as it exists in the dictionary object. In the following example, it is likely that the second case will be executed, but the first case actually executed:
def if_action(dic): match dic: case {"action": action}: print("action: %s, no object" % action) case {"action": action, "object": _}: print("action: %s, have object" % action) if_action({"action": "create", "object": "user1"}) # action: create, no object
When the matching object is a class object, the matching rule is similar to a dictionary (dict). As long as the object type and the attributes of the object satisfy the criteria, it is likely that the second case will be executed, but the first case actually executed:
class Info: def __init__(self, name, age): self.name, self.age = name, age def get_info(people): match people: case Info(name="Bob"): print("case 1") case Info(name="Bob", age="20"): print("case 2") people = Info(name="Bob", age="20") get_info(people) # case 1
PEP 604: New Union Types
Python is a weakly typed language, but in Python 3, there is support for defining the type of parameters and returns:
def test(a: int) -> int: return a**2
Usually a parameter and return value can only be one type. In C/C++, Java, Go and other static languages, it is not possible to return two types or to use two types for a parameter, but in Python it is possible:
def test(a: str or int) -> str or int: return a**2
The or writing here looks very uncomfortable, so the typing module was introduced in Python 3.5 and Uinon is recommended:
from typing import Union def test(a: Union[str, int]) -> Union[str, int]: return a**2
In this Python 3.10.0 update, PEP 604 Allow Union Types to be written as X | Y:
def test(a: str | int) -> str | int: return a**2
The new operator can also be used as the second parameter for isinstance() and issubclass():
print(isinstance(5, int | str)) # True print(isinstance(None, int | None)) # True print(issubclass(bool, int | float)) # True print(isinstance(42, None | str)) # False
PEP 626: Error debugging accurate to line
stay PEP 626 In previous versions, error messages generally pointed to the next line instead of the actual error location, and now they can point to the exact location of the error code.
Error code example 1:
li = [1, 2, 3
Previous version error:
File "D:\python3Project\test.py", line 5 ^ SyntaxError: unexpected EOF while parsing
Python version 3.10 error:
File "D:\python310Project\test.py", line 4 li = [1, 2, 3 ^ SyntaxError: '[' was never closed
Error code example 2:
expected = {"name": "Bob", "age": 20 some_other_code = foo()
Previous version error:
File "D:\python3Project\test.py", line 2 some_other_code = foo() ^ SyntaxError: invalid syntax
Python version 3.10 error:
File "D:\python310Project\test.py", line 1 expected = {"name": "Bob", "age": 20 ^ SyntaxError: '{' was never closed
PEP 618:zip() Optional Length Check
zip() is a built-in function in Python that takes an iterative object as a parameter, packages its corresponding elements into tuples, and returns a list of those tuples.
In previous versions, if the number of elements in each iterator was inconsistent, the return list was the same length as the shortest object, as shown in the following example:
a = [1, 2, 3] b = [4, 5, 6] c = [4, 5, 6, 7, 8] zipped1 = zip(a, b) zipped2 = zip(a, c) # Number of elements is consistent with the shortest list print([z for z in zipped1]) # [(1, 4), (2, 5), (3, 6)] print([z for z in zipped2]) # [(1, 4), (2, 5), (3, 6)]
stay PEP 618 In, a strict parameter has been added, and when set to True, the two Iterable items passed in zip() must have the same length or ValueError will be thrown
a = [1, 2, 3] b = [4, 5, 6] c = [4, 5, 6, 7, 8] zipped1 = zip(a, b, strict=True) zipped2 = zip(a, c, strict=True) print([z for z in zipped1]) print([z for z in zipped2])
Report errors:
[(1, 4), (2, 5), (3, 6)] Traceback (most recent call last): File "D:\python310Project\test.py", line 8, in <module> print([z for z in zipped2]) File "D:\python310Project\test.py", line 8, in <listcomp> print([z for z in zipped2]) ValueError: zip() argument 2 is longer than argument 1
BPO-12782: Allow bracketed context managers
Python Context Manager is very useful for opening/closing files, working with database connections, and many other things. In Python 3.10.0, their syntax will be improved a little bit in high quality. BPO-12782 Formally allowed bracketed context managers, you can now create multiple lines with one with statement, as shown below:
with( open("text1.txt", encoding="utf-8") as f1, open("text2.txt", encoding="utf-8") as f2 ): print(f1.read(), f2.read())
PEP 613: Explicit type alias
PEP 613 Use TypeAlias to explicitly label type aliases to improve readability.
Previous versions, you can see that x can be easily confused:
x = int def plus_int(a: x, b: x) -> x: return a+b
In Python 3.10, using TypeAlias indicates that this is an alias to disambiguate:
from typing import TypeAlias x: TypeAlias = int def plus_int(a: x, b: x) -> x: return a+b
Performance Improvement
Like all the latest versions of Python, Python 3.10 also brings some performance improvements. The first is to optimize str(), bytes(), and bytearray() constructors, which are about 30 percent faster, with the code extracted from BOP-41334:
$ ./python -m pyperf timeit -q --compare-to=../cpython-release2/python "str()" Mean +- std dev: [/home/serhiy/py/cpython-release2/python] 81.9 ns +- 4.5 ns -> [/home/serhiy/py/cpython-release/python] 60.0 ns +- 1.9 ns: 1.36x faster (-27%) $ ./python -m pyperf timeit -q --compare-to=../cpython-release2/python "bytes()" Mean +- std dev: [/home/serhiy/py/cpython-release2/python] 85.1 ns +- 2.2 ns -> [/home/serhiy/py/cpython-release/python] 60.2 ns +- 2.3 ns: 1.41x faster (-29%) $ ./python -m pyperf timeit -q --compare-to=../cpython-release2/python "bytearray()" Mean +- std dev: [/home/serhiy/py/cpython-release2/python] 93.5 ns +- 2.1 ns -> [/home/serhiy/py/cpython-release/python] 73.1 ns +- 1.8 ns: 1.28x faster (-22%)
Another, more notable optimization (if you use type annotations) is that function parameters and their comments are no longer computed at runtime but at compile time, which makes it about twice as fast to create a function with parameter annotations.
In addition, there are many more optimizations for each part of the Python core, and you can find more details in the following Python bug tracker questions: BPO-41718,BPO-42927,BPO-43452.
Other changes
- PEP 623 : Deprecate and prepare to delete wstr members from PyUnicodeObject.
- PEP 612 : Parameter specification variable.
- PEP 632 : Discard the distutils module and recommend setuptools.
- PEP 644 : The standard library for CPython only supports OpenSSL Version 1.1.1 or later.
- PEP 624 : Delete Py_UNICODE encoder API.
- PEP 597 : Add an optional Encoding Warning.