current position:Home>Python type hints from introduction to practice
Python type hints from introduction to practice
2022-01-30 10:09:10 【Shoot the clouds again】
Python I think everyone is familiar with it , Even arguments about whether it is useful or useless may have tired of reading . But anyway , As a language that will be added to the college entrance examination, it still has its uniqueness , Today we'll talk again Python.
Python Is a dynamic strongly typed language
《 smooth Python》 It is mentioned in the book , If a language rarely implicitly converts types , So it's a strongly typed language , for example Java、C++ and Python Is a strongly typed language .
At the same time, if a language often implicitly converts types , So it's a weakly typed language ,PHP、JavaScript and Perl It's a weakly typed language .
Of course, the above simple example comparison , Not exactly Python Is a strong type of language , because Java Also supports integer and string Add operation , And Java It's a strongly typed language . therefore 《 smooth Python》 There are also definitions of static and dynamic types : The language for checking type at compile time is static type language , The language for checking types at run time is dynamic type language . Static languages require declaration types ( Some modern languages use type derivation to avoid partial type declarations ).
in summary , About Python Dynamic strongly typed languages are obvious and uncontroversial .
Type Hints On
Python stay PEP 484(Python Enhancement Proposals,Python Enhancement proposal )[www.python.org/dev/peps/pe… Type Hints( Type notes ). Further strengthened Python Is a feature of a strongly typed language , It's in Python3.5 It's the first time that . Use Type Hints Let's write a with types Python Code , It looks more in line with the strongly typed language style .
There are two definitions greeting function :
- The common expression is as follows :
name = "world"
def greeting(name):
return "Hello " + name
greeting(name)
Copy code
- Joined the Type Hints Let's write it as follows :
name: str = "world"
def greeting(name: str) -> str:
return "Hello " + name
greeting(name)
Copy code
With PyCharm For example , In the process of writing code IDE It will label according to the type of function , Type check the parameters passed to the function . If you find that the argument type does not match the formal parameter type label of the function, you will be prompted as follows :
Common data structures Type Hints How to write it
Through a greeting The function shows Type Hints Usage of , Next we will Python Common data structures Type Hints Write more in-depth study .
Default parameters
Python Function supports default arguments , The following are the default parameters Type Hints How to write it , Just write the type between the variable and the default parameter .
def greeting(name: str = "world") -> str:
return "Hello " + name
greeting()
Copy code
Custom type
For custom types ,Type Hints Can also support . Its writing is similar to Python There is no difference between built-in types .
class Student(object):
def __init__(self, name, age):
self.name = name
self.age = age
def student_to_string(s: Student) -> str:
return f"student name: {s.name}, age: {s.age}."
student_to_string(Student("Tim", 18))
Copy code
When the type is labeled as a custom type ,IDE You can also check the type .
Container type
When we want to add type labels to built-in container types , Because of the type annotation operator [] stay Python Represents the slice operation in , Therefore, it will cause syntax errors . So you can't use built-in container types directly as annotations , Need from typing Import the corresponding container type annotation in the module ( Usually the initial capital form of the built-in type ).
from typing import List, Tuple, Dict
l: List[int] = [1, 2, 3]
t: Tuple[str, ...] = ("a", "b")
d: Dict[str, int] = {
"a": 1,
"b": 2,
}
Copy code
however PEP 585[www.python.org/dev/peps/pe… Python Built in type , Without syntax errors .
l: list[int] = [1, 2, 3]
t: tuple[str, ...] = ("a", "b")
d: dict[str, int] = {
"a": 1,
"b": 2,
}
Copy code
Type the alias
Some complex nested types are long to write , If there is repetition , It will be painful , The code will not be clean enough .
config: list[tuple[str, int], dict[str, str]] = [
("127.0.0.1", 8080),
{
"MYSQL_DB": "db",
"MYSQL_USER": "user",
"MYSQL_PASS": "pass",
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
},
]
def start_server(config: list[tuple[str, int], dict[str, str]]) -> None:
...
start_server(config)
Copy code
This can be solved by aliasing the type , Similar variable naming .
Config = list[tuple[str, int], dict[str, str]]
config: Config = [
("127.0.0.1", 8080),
{
"MYSQL_DB": "db",
"MYSQL_USER": "user",
"MYSQL_PASS": "pass",
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
},
]
def start_server(config: Config) -> None:
...
start_server(config)
Copy code
This makes the code look much more comfortable .
Variable parameters
Python A very flexible aspect of functions is that they support variable parameters ,Type Hints It also supports variable parameter type annotation .
def foo(*args: str, **kwargs: int) -> None:
...
foo("a", "b", 1, x=2, y="c")
Copy code
IDE Can still check out .
Generic
Using dynamic languages requires generic support ,Type Hints There are also many solutions for generics .
TypeVar
Use TypeVar Can accept any type of .
from typing import TypeVar
T = TypeVar("T")
def foo(*args: T, **kwargs: T) -> None:
...
foo("a", "b", 1, x=2, y="c")
Copy code
Union
If you don't want to use generics , Just want to use several specified types , Then you can use Union To do it . Like defining concat The function just wants to receive str or bytes type .
from typing import Union
T = Union[str, bytes]
def concat(s1: T, s2: T) -> T:
return s1 + s2
concat("hello", "world")
concat(b"hello", b"world")
concat("hello", b"world")
concat(b"hello", "world")
Copy code
IDE The inspection prompt is shown in the figure below :
TypeVar and Union difference
TypeVar Not only can you receive generics , It can also be like Union The use of , You just need to pass the type range you want to specify as a parameter in turn when instantiating . Follow Union The difference is , Use TypeVar Declared function , Multiple parameter types must be the same , and Union No restrictions .
from typing import TypeVar
T = TypeVar("T", str, bytes)
def concat(s1: T, s2: T) -> T:
return s1 + s2
concat("hello", "world")
concat(b"hello", b"world")
concat("hello", b"world")
Copy code
Here are the USES TypeVar When defining types IDE Tips :
Optional
Type Hints Provides Optional As a Union[X, None] Short form of , Indicates that the marked parameter is either X type , Either for None,Optional[X] Equivalent to Union[X, None].
from typing import Optional, Union
# None => type(None)
def foo(arg: Union[int, None] = None) -> None:
...
def foo(arg: Optional[int] = None) -> None:
...
Copy code
Any
Any It's a special type , Can represent all types of . Function with return value and parameter type not specified , All implicitly default to Any, So the following two greeting Function writing is equivalent :
from typing import Any
def greeting(name):
return "Hello " + name
def greeting(name: Any) -> Any:
return "Hello " + name
Copy code
When we both want to use Type Hints To implement the writing of static types , And don't want to lose the flexibility unique to dynamic languages , You can use Any.
Any When a type value is assigned to a more precise type , Do not perform type checking , The following code IDE There will be no error message :
from typing import Any
a: Any = None
a = [] # Dynamic language features
a = 2
s: str = ''
s = a # Any Type values are assigned to more precise types
Copy code
Callable object ( function 、 Class etc. )
Python Any callable type in can use Callable Annotate . The following code is marked Callable[[int], str],[int] Parameter list representing callable types ,str Represents the return value .
from typing import Callable
def int_to_str(i: int) -> str:
return str(i)
def f(fn: Callable[[int], str], i: int) -> str:
return fn(i)
f(int_to_str, 2)
Copy code
Quote from
When we need to define a tree structure , Self reference is often required . When executed init When the method is used Tree Type has not been generated yet , So it can't be used like str Like this built-in type, you can label directly , Need to be in string form “Tree” To reference an object that has not been generated .
class Tree(object):
def __init__(self, left: "Tree" = None, right: "Tree" = None):
self.left = left
self.right = right
tree1 = Tree(Tree(), Tree())
Copy code
IDE Self reference types can also be checked .
This form can not only be used for self reference , Preceding references also apply .
The duck type
Python One notable feature is its extensive use of duck types ,Type Hints Provides Protocol To support duck types . When defining a class, you only need to inherit Protocol You can declare an interface type , When encountering annotation of interface type , As long as the received object implements all methods of the interface type , You can check the type annotation ,IDE There will be no error . there Stream No explicit inheritance is required Interface class , It just needs to be done close The method can .
from typing import Protocol
class Interface(Protocol):
def close(self) -> None:
...
# class Stream(Interface):
class Stream:
def close(self) -> None:
...
def close_resource(r: Interface) -> None:
r.close()
f = open("a.txt")
close_resource(f)
s: Stream = Stream()
close_resource(s)
Copy code
Because of the built-in open Function returns the file object and Stream Objects all implement close Method , So you can pass Type Hints The inspection of , And strings “s” It didn't happen close Method , therefore IDE Type error will be prompted .
Type Hints Other ways of writing
actually Type Hints There is not only one way to write ,Python In order to be compatible with different people's preferences and the migration of old code, two other writing methods are implemented .
Write with comments
Come and have a look at tornado Examples of frameworks (tornado/web.py). It is applicable to modification of existing projects , The code is written , Type labels need to be added later .
Write in a separate file (.pyi)
You can create a new one in the same directory as the source code .py Homonymous .pyi file ,IDE It can also automatically do type checking . The advantage of this is that you can make no changes to the original code , Completely decoupled . The disadvantage is that it is equivalent to maintaining two copies of code at the same time .
Type Hints practice
Basically , Commonly used in daily coding Type Hints The writing methods have been introduced to you , Let's take a look at how to apply it in practical coding Type Hints.
dataclass—— Data class
dataclass It's a decorator , It can decorate classes , Used to add magic methods to classes , for example init() and repr() etc. , It's in PEP 557[www.python.org/dev/peps/pe…
from dataclasses import dataclass, field
@dataclass
class User(object):
id: int
name: str
friends: list[int] = field(default_factory=list)
data = {
"id": 123,
"name": "Tim",
}
user = User(**data)
print(user.id, user.name, user.friends)
# > 123 Tim []
Copy code
Above use dataclass The code written is equivalent to the following code :
class User(object):
def __init__(self, id: int, name: str, friends=None):
self.id = id
self.name = name
self.friends = friends or []
data = {
"id": 123,
"name": "Tim",
}
user = User(**data)
print(user.id, user.name, user.friends)
# > 123 Tim []
Copy code
Be careful :dataclass Field types are not checked .
You can find , Use dataclass Writing classes can reduce a lot of duplicate template code , Grammar is also clearer .
Pydantic
Pydantic It's based on Python Type Hints Third party library , It provides data validation 、 Serialization and documentation capabilities , It is a library worth learning from . Here's a use of Pydantic Example code for :
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
class User(BaseModel):
id: int
name = 'John Doe'
signup_ts: Optional[datetime] = None
friends: list[int] = []
external_data = {
'id': '123',
'signup_ts': '2021-09-02 17:00',
'friends': [1, 2, '3'],
}
user = User(**external_data)
print(user.id)
# > 123
print(repr(user.signup_ts))
# > datetime.datetime(2021, 9, 2, 17, 0)
print(user.friends)
# > [1, 2, 3]
print(user.dict())
"""
{
'id': 123,
'signup_ts': datetime.datetime(2021, 9, 2, 17, 0),
'friends': [1, 2, 3],
'name': 'John Doe',
}
"""
Copy code
Be careful :Pydantic The field type will be forcibly checked .
Pydantic It's written like dataclass Very similar , But it does more extra work , It also provides information such as .dict() This is a very convenient way .
Let's see another one Pydantic An example of data validation , When User When the parameter received by the class does not meet the expectation , Will throw out ValidationError abnormal , The exception object provides .json() This method is convenient to view the cause of the exception .
from pydantic import ValidationError
try:
User(signup_ts='broken', friends=[1, 2, 'not number'])
except ValidationError as e:
print(e.json())
"""
[
{
"loc": [
"id"
],
"msg": "field required",
"type": "value_error.missing"
},
{
"loc": [
"signup_ts"
],
"msg": "invalid datetime format",
"type": "value_error.datetime"
},
{
"loc": [
"friends",
2
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
]
"""
Copy code
All error messages are saved in one list in , The error report of each field is saved in the nested dict in , among loc Identifies the exception field and error reporting location ,msg It is an error message ,type Is the error reporting type , In this way, the whole reason for the error report is clear at a glance .
MySQLHandler
MySQLHandler[github.com/jianghushin… pymysql Encapsulation of Libraries , Make it support the use of with Syntax call execute Method , And the query results from tuple Replace with object, It's also true Type Hints Application .
class MySQLHandler(object):
"""MySQL handler"""
def __init__(self):
self.conn = pymysql.connect(
host=DB_HOST,
port=DB_PORT,
user=DB_USER,
password=DB_PASS,
database=DB_NAME,
charset=DB_CHARSET,
client_flag=CLIENT.MULTI_STATEMENTS, # execute multi sql statements
)
self.cursor = self.conn.cursor()
def __del__(self):
self.cursor.close()
self.conn.close()
@contextmanager
def execute(self):
try:
yield self.cursor.execute
self.conn.commit()
except Exception as e:
self.conn.rollback()
logging.exception(e)
@contextmanager
def executemany(self):
try:
yield self.cursor.executemany
self.conn.commit()
except Exception as e:
self.conn.rollback()
logging.exception(e)
def _tuple_to_object(self, data: List[tuple]) -> List[FetchObject]:
obj_list = []
attrs = [desc[0] for desc in self.cursor.description]
for i in data:
obj = FetchObject()
for attr, value in zip(attrs, i):
setattr(obj, attr, value)
obj_list.append(obj)
return obj_list
def fetchone(self) -> Optional[FetchObject]:
result = self.cursor.fetchone()
return self._tuple_to_object([result])[0] if result else None
def fetchmany(self, size: Optional[int] = None) -> Optional[List[FetchObject]]:
result = self.cursor.fetchmany(size)
return self._tuple_to_object(result) if result else None
def fetchall(self) -> Optional[List[FetchObject]]:
result = self.cursor.fetchall()
return self._tuple_to_object(result) if result else None
Copy code
Run time type check
Type Hints The name Hints instead of Check, Because it's just a type of prompt, not a real check . Shown above Type Hints usage , In fact, it's all IDE It's helping us complete the function of type checking , But actually ,IDE Type checking does not determine whether an error is reported during code execution , The function of syntax check prompt can only be achieved in the static period .
To implement mandatory type checking during code execution , We need to write our own code or introduce a third-party library ( As described above Pydantic). Now I pass a type_check The function implements the runtime dynamic check type , For your reference :
from inspect import getfullargspec
from functools import wraps
from typing import get_type_hints
def type_check(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
fn_args = getfullargspec(fn)[0]
kwargs.update(dict(zip(fn_args, args)))
hints = get_type_hints(fn)
hints.pop("return", None)
for name, type_ in hints.items():
if not isinstance(kwargs[name], type_):
raise TypeError(f"expected {type_.__name__}, got {type(kwargs[name]).__name__} instead")
return fn(**kwargs)
return wrapper
# name: str = "world"
name: int = 2
@type_check
def greeting(name: str) -> str:
return str(name)
print(greeting(name))
# > TypeError: expected str, got int instead
Copy code
As long as give greeting The function is marked with type_check Decorator , Run time type checking can be realized .
appendix
If you want to continue to learn more about using Python Type Hints, Here are some open source projects I recommend for your reference :
-
Pydantic [github.com/samuelcolvi…]
-
FastAPI [github.com/tiangolo/fa…]
-
Tornado [github.com/tornadoweb/…]
-
Flask [github.com/pallets/fla…]
-
Chia-pool [github.com/Chia-Networ…]
-
MySQLHandler [github.com/jianghushin…]
Recommended reading
Practical experience sharing : Use PyO3 To build your Python modular
copyright notice
author[Shoot the clouds again],Please bring the original link to reprint, thank you.
https://en.pythonmana.com/2022/01/202201301009081708.html
The sidebar is recommended
- Similarities and differences of five pandas combinatorial functions
- Python beginner's eighth day ()
- Necessary knowledge of Python: take you to learn regular expressions from zero
- Get your girlfriend's chat records with Python and solve the paranoia with one move
- My new book "Python 3 web crawler development practice (Second Edition)" has been recommended by the father of Python!
- From zero to familiarity, it will take you to master the use of Python len() function
- Python type hint type annotation guide
- leetcode 108. Convert Sorted Array to Binary Search Tree(python)
- For the geometric transformation of Python OpenCV image, let's first talk about the extraordinary resize function
- leetcode 701. Insert into a Binary Search Tree (python)
guess what you like
-
For another 3 days, I sorted out 80 Python datetime examples, which must be collected!
-
Python crawler actual combat | using multithreading to crawl lol HD Wallpaper
-
Complete a python game in 28 minutes, "customer service play over the president card"
-
The universal Python praise machine (commonly known as the brushing machine) in the whole network. Do you want to know the principle? After reading this article, you can write one yourself
-
How does Python compare file differences between two paths
-
Common OS operations for Python
-
[Python data structure series] linear table - explanation of knowledge points + code implementation
-
How Python parses web pages using BS4
-
How do Python Network requests pass parameters
-
Python core programming - decorator
Random recommended
- Python Network Programming -- create a simple UPD socket to realize mutual communication between two processes
- leetcode 110. Balanced Binary Tree(python)
- Django uses Django celery beat to dynamically add scheduled tasks
- The bear child said "you haven't seen Altman" and hurriedly studied it in Python. Unexpectedly
- Optimization iteration of nearest neighbor interpolation and bilinear interpolation algorithm for Python OpenCV image
- Bilinear interpolation algorithm for Python OpenCV image, the most detailed algorithm description in the whole network
- Use of Python partial()
- Python game development, pyGame module, python implementation of angry birds
- leetcode 1104. Path In Zigzag Labelled Binary Tree(python)
- Save time and effort. 10 lines of Python code automatically clean up duplicate files in the computer
- Learn python, know more meat, and be a "meat expert" in the technical circle. One article is enough
- [Python data structure series] "stack (sequential stack and chain stack)" -- Explanation of knowledge points + code implementation
- Datetime module of Python time series
- Python encrypts and decrypts des to solve the problem of inconsistency with Java results
- Chapter 1: introduction to Python programming-4 Hello World
- Summary of Python technical points
- 11.5K Star! An open source Python static type checking Library
- Chapter 2: Fundamentals of python-1 grammar
- [Python daily homework] day4: write a function to count the number of occurrences of each number in the incoming list and return the corresponding dictionary.
- Python uses turtle to express white
- Some people say Python does not support function overloading?
- "Python instance" was shocked and realized the dirty words and advertisement detection of the chat system with Python
- Introduction to Python - CONDA common commands
- Python actual combat | just "4 steps" to get started with web crawler (with benefits)
- Don't know what to eat every day? Python to tell you! Generate recipes and don't worry about what to eat every day!
- Are people who like drinking tea successful? I use Python to make a tea guide! Do you like it?
- I took 100g pictures offline overnight with Python just to prevent the website from disappearing
- Binary operation of Python OpenCV image re learning and image smoothing (convolution processing)
- Analysis of Python event mechanism
- Iterator of Python basic language
- Base64 encryption and decryption in Python
- Chapter 2: Fundamentals of python-2 variable
- Python garbage collection summary
- Python game development, pyGame module, python takes you to realize a magic tower game from scratch (1)
- Python draws a spinning windmill with turtle
- Deep understanding of Python features
- A website full of temptations for Python crawler writers, "lovely picture network", look at the name of this website
- Python opencv Canny edge detection knowledge supplement
- Complex learning of Python opencv Sobel operator, ScHARR operator and Laplacian operator
- Python: faker extension package