Python 学习笔记——11.类型注解和帮助系统
虽然 Python
作为动态语言具有极高的灵活性,编程非常便利,但是为了让程序更加清晰可读,有必要加入类型注解,要求
Python 3.9+,一些关于类型注解需要的内容在 typing
模块,在高版本中部分情况可以省略导入typing
模块。
类型注解不是强制的,并不会真正影响Python脚本的执行,但是静态代码检查可以基于类型注解提供警告,以提高编程的可靠性。这种类型检查的使用也很繁琐,容易误报警告,过度使用类型注解会丧失Python的灵活性。
变量注解
在定义变量时可以注解它的类型,这对静态代码分析很有帮助,例如
1 | age: int = 20 |
第二行会标红:静态检查不通过,但是代码仍然可以正常执行。
注意:
- 在 VScode+jupyter 中,只有处于同一个 cell 的代码才会执行静态检查,不同 cell 之间不会进行检查。
- 这种变量类型注解写错了可能找不出来,例如
age: float = 20
,这不会影响程序执行效果,但是会误导静态分析。
例如几种基本的类型
1 | a: int = 3 |
对于列表,字典,元组等复合类型,直接使用 list
或
dict
不能体现其中元素的类型,可以使用下面的形式(注意是中括号,并且要求
Python 版本很高)
1 | a: list[int] = [1, 2, 3] |
注意 list
支持不同的类型元素,可以使用如下语法
1 | from typing import Union |
或者对于 Python 3.10+,可以直接使用
1 | a: list[int|str] = [1, 'a', 2] |
1 | def mix(scores: list[int], ages: dict[str, int]) -> tuple[int, int]: |
函数注解
对函数的注解包括两部分:参数注解和函数返回值注解,对函数的注解是最常用的需求。
几个简单的例子如下 1
2
3
4
5
6
7
8def do_nothing() -> None:
pass
def say_hi(name: str) -> str:
return f'Hello {name}!'
def add(first: int = 10, second: float = 5.5) -> float:
return first + second
关于函数返回值的注解,考虑几个特殊情形:
可选返回值,返回某个类型或者 None,例如
1
2
3
4
5
6
7from typing import Optional
def foo(a: int = 0) -> Optional[str]:
if a == 1:
return 'Yeah'
else:
return None # 或者省略,隐式返回None多个返回值,可能返回某几个类型之一,例如
1
2
3
4
5
6
7
8
9from typing import Union
def foo(a: int = 0) -> Union[str, int, float]:
if a == 1:
return 'h'
elif a == 2:
return 1
else:
return 1.0无返回值,注意默认情形下 Python 的函数返回 None,无返回值只用于必然引起异常的情形,例如
1
2
3
4from typing import NoReturn
def hello() -> NoReturn:
raise RuntimeError('oh no')
可调用对象注解
对于可调用对象(函数,自定义类等)自身,可以使用
Callable
类型注解
1 | from typing import Callable |
可以指定可调用对象的细节,包括参数类型和返回值类型,例如
1 | from typing import Callable |
可以如下使用
1 | def t1() -> str: |
自定义类型注解
在类型注解时可以使用自定义的类型,例如 1
2
3
4
5
6
7class Person:
def __init__(self, name: str):
self.name = name
def hello(p: Person) -> str:
return f'Hello, {p.name}'
有时类的定义尚未实现,或者为了避免循环依赖,可以使用同名的字符串代替
1
2
3
4
5
6
7def hello(p: 'Person') -> str:
return f'Hello, {p.name}'
class Person:
def __init__(self, name: str):
self.name = name
万能注解Any
万能的 Any
类型注解,用在不知道写什么注解的时候,例如
1 | from typing import Any |
帮助系统
获取帮助
Python 可以通过如下方式获取帮助:
help()
函数,可以查看函数、类和模块等提供的帮助信息。__doc__
属性,可以获取函数、类和模块等提供的帮助信息,这个属性不需要手动实现,只要在定义函数、类、模块时写了三引号包裹的字符串文档,就会自动将其设置为__doc__
属性。(函数和模块也是对象,因此也有属性)- 在 IPython 以及 Jupyter Notebook 中还可以使用
?
来获取帮助(与help()
并不等价),使用??
甚至会尝试提供源码(如果基于 Python 实现的)
对于内置函数的帮助,例如 help(len)
输出信息如下
1
2
3
4Help on built-in function len in module builtins:
len(obj, /)
Return the number of items in a container.
len?
输出信息如下 1
2
3Signature: len(obj, /)
Docstring: Return the number of items in a container.
Type: builtin_function_or_method
len.__doc__
的内容如下 1
Return the number of items in a container.
由此可见,这几种方式实际都是在输出__doc__
属性字符串,只是形式略有不同。
对于类型同理,str?
输出内容如下 1
2
3
4
5
6
7
8
9
10
11
12
13
14Init signature: str(self, /, *args, **kwargs)
Docstring:
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str
Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to sys.getdefaultencoding().
errors defaults to 'strict'.
Type: type
Subclasses: StrEnum, DeferredConfigString, FoldedCase, _rstr, _ScriptTarget, _ModuleTarget, LSString, include, Keys, InputMode, ...
其中最主要的 Docstring 就是 str.__doc__
的内容,help(str)
的输出则非常长,不仅包括 str
自身的说明,还有所有方法的签名和说明,以及所有属性的说明。
支持帮助
我们自己编写的代码为了规范化和降低使用难度,也需要提供对应的帮助信息,下面对函数和自定义类型分别举例。
函数的帮助信息例如 1
2
3
4
5
6
7
8
9
10
11
12
13def add(x, y):
"""
Add two numbers and return the result.
Parameters:
x (int): First number.
y (int): Second number.
Returns:
int: The sum of x and y.
"""
return x + y
自定义类型的帮助信息例如 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21class Person:
"""
Represents a person with a name and age.
"""
def __init__(self, name, age):
"""
Initialize a new Person.
Parameters:
name (str): The person's name.
age (int): The person's age.
"""
self.name = name
self.age = age
def greet(self):
"""
Print a greeting with the person's name.
"""
print(f"Hello, my name is {self.name}.")