Python3中类型注解的学习与实践(python3注释)
Python3中类型注解的学习与实践(python3注释)
本文中主要会介绍Python3中的类型注解的基础概念以及日常使用的技巧。本文主要面对的人群为有着"想对代码实施规范化管理以及增加可读性的”的人们。笔者也会将自己的理解在文中进行阐述,这也算是在和大家交流心得的一个过程。若文中有错误的理解和概念,请大家及时纠正;吸纳大家的建议,对于我来说也是很重要的学习过程之一。
1. 概述
Tips: Python的动态类型、一切即为对象概念的体现。
2.语法
2.1 标准语法
在编写新代码时最常用的写法,例如:
def greeting(name: str) -> str: return 'Hello ' + name
2.2 注释语法
该写法比较适合于在老项目上使用,即通过将类型注解写在注释里的方式来实现。例如:
path = None # type: list[str]
2.3 使用单独文件.pyi来编写类型注解
可以在源代码相同的目录下新建一个与 .py 同名的 .pyi 文件,IDE 同样能够自动做类型检查。该方式的优点为对原来的代码不做任何改动,完全解耦;缺点是相当于要同时维护两份代码。
Tips: 很多大型开源项目都使用该方法进行类型注解的编写。
在编写时,除了使用…来代替函数或对象主体内容以外,其他编写方式都与正常编写代码的语法是相同的。例如:
class WebDriver(RemoteWebDriver): service: Any def __init__( self, executable_path: str = ..., port: int = ..., options: Any | None = ..., service_args: Any | None = ..., desired_capabilities: Any | None = ..., service_log_path: Any | None = ..., chrome_options: Any | None = ..., keep_alive: bool = ..., ) -> None: ... def launch_app(self, id): ... def get_network_conditions(self): ... def set_network_conditions(self, **network_conditions) -> None: ... def execute_cdp_cmd(self, cmd, cmd_args): ... def quit(self) -> None: ... def create_options(self): ...
3. 注解规范
本章节内会介绍一下常用类型的注解方式。
3.1 自定义类的注解
若变量或方法返回值为自定义的类对象,则直接使用自定义类名进行注解即可。
3.2 容器类型注解
需要从 typing 模块中导入对应的容器类型注解:
from typing import List, Tuple, Dict
但PEP 585后就可以直接使用Python 的内置类型来注解,例如:
list[int] = [1, 2, 3] tuple[str, ...] = ("a", "b") dict[str, int] = { "a": 1,"b": 2}
3.3 注解别名
若注解过长,可以通过给类型注解起别名来解决,例如:
Config = list[tuple[str, int], dict[str, str]] def start_server(config: Config) -> None:
3.4 可变参数
注解方式例如:
def foo(*args: str, **kwargs: int) -> None:
3.5 泛型注解
泛型注解主要用于解决对象的类型信息不能以静态方式写明的情况,即变量类型会随着业务状态而变化,并且无法确定其类型的变化范围。例如,不能确定list或者dict中都包含哪些类型的变量,这时候就可以使用泛型来解决:
from typing import TypeVar T = TypeVar('T') # T代表任意类型 S = TypeVar('S', int, str) # S代表是int或者是string类型 def first(l: Sequence[T]) -> T: # Generic function return l[0]
3.6 指定类型(常用)
如果能够确定变量的类型范围,那么可以使用 Union 来进行注解。
from typing import Union T = Union[str, bytes] # 表示只能是str或者bytes
再如,如果一个参数支持使用多种类型,则可以使用Union来注释:
record: Union[ str, Iterable['str'], Point, Iterable['Point'], dict, Iterable['dict'], bytes, Iterable['bytes'], Observable, NamedTuple, Iterable['NamedTuple'], 'dataclass', Iterable['dataclass'] ] = None
3.7 Optional
例如:
Optional[X] = Union[X, None] # 表示被标注的参数要么为 X 类型,要么为 None
3.8 Any(慎用)
一般用于当不知道如何编写类型注解但又不必须使用类型注解式写法的时候使用。
from typing import Any def greeting(name: Any) -> Any: return "Hello " + name
Tips: 笔者认为,Any的使用要有克制性,因为其本质和不使用类型注解语法是相同的;只是为了迎合整篇代码的类型注解风格。往往Any都是使用在对相关的代码逻辑不太清楚的位置。可以使用Any来作为临时注解,但后续读懂相关逻辑后,应将Any更改为具体的类型注解。
3.9 可调用对象
因为python支持函数式编程,因此有些方法的形参可能为函数;Callable类型注解就是用描述这种类型的变量。例如:
def f(fn: Callable[[int], str], i: int) -> str: '''Callable[[int], str]表示该方法的形参或构造方法的形参必须是一个int类型变量,该方法的返回值是string类型变量 ''' return fn(i)
3.10 自引用
该注解类型常用于类似于递归调用方式的方法。由于在进行第一次递归调用的时候,需要调用的方法还没有生成,因此不能使用其他的静态注解方式直接写明,而是需要使用字符串形式来注解。例如:
class Tree(object): def __init__(self, left: "Tree" = None, right: "Tree" = None): self.left = left self.right = right
3.11 鸭子类型
from typing import Protocol
当遇到接口类型的注解时,只要接收到的对象实现了接口类型的所有方法,即可通过类型注解的检查。
3.12 返回多个变量(常用)
使用typing.Tuple类型来注解返回多个值的函数的返回值。因为函数返回多个值的本质就是Python将多个值组成一个tuple返回,因此使用typing模块中的Tuple类型来注解。
from typing import Any, Tuple def multiple_bind(self, ec2_instances: list[str], ip_label: str) -> Tuple[list[str], dict[str, str]]: pass
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~