typing模块学习
Dict, Tuple, List, Optional, Union
新版本的python中,不用在使用typing模块中的Dict Tuple, List等对象了,直接使用原生的类型去做类型提示。
Optional, Union等都可以使用管道提示符来代替。 例如Optional[str] 相当于 str|None
使用mypy xxx.py命令来检查变量类型的使用是否符合声明的类型提示符
自定义class
自定义class,类型提示为基类时可传递子类
class Animal:
name: str
def speak(self) -> str:
return "Some sound"
class Dog(Animal):
def speak(self) -> str:
return "Woof"
class Cat(Animal):
def speak(self) -> str:
return "Meow"
def talk_animal(animal_class: Animal) -> None:
print(animal_class.speak())
talk_animal(Dog()) # 函数参数为基类,传子类ok
def talk_cat(animal_class: Cat) -> None:
print(animal_class.speak())
talk_cat(Dog()) # 函数参数为一个子类,传另一个子类,类型提示会报错
def talk_dog(animal_class: Dog) -> None:
print(animal_class.speak())
talk_dog(Animal()) # 函数参数为子类,传父类,类型提示会报错
Type
用法, 用于表示类对象的类型提示
from typing import Type
class Animal:
def speak(self) -> str:
return "Some sound"
class Dog(Animal):
def speak(self) -> str:
return "Woof"
class Cat(Animal):
def speak(self) -> str:
return "Meow"
# 这个函数接受一个 Animal 类或其子类的类型,并返回一个实例
def create_animal(animal_class: Type[Animal]) -> Animal:
return animal_class()
dog = create_animal(Dog)
print(dog.speak())
Any
代表任何类型,等同于无类型提示
from typing import Any
# 但即便是等同于无类型提示,显示的提示总比没有提示要好
def process_data(data: Any) -> Any:
if isinstance(data, str):
return data.upper()
elif isinstance(data, int):
return data * 2
else:
return data
# Any可以用在其它容器类型中做类型提示
def get_first_element(data: list[Any]) -> Any:
if data:
return data[0]
else:
return None
Sequence
Sequence
是 Python typing
模块中的一个抽象基类,用于表示支持序列操作的类型。这包括所有的列表、元组、字符串等。这种抽象类型允许你编写更通用的代码,可以接受任何符合序列协议的对象,而不仅仅是特定的容器类型。
from typing import Sequence
def concatenate(elements: Sequence[str]) -> str:
return ''.join(elements)
# 使用列表
print(concatenate(['a', 'b', 'c'])) # 输出: abc
# 使用元组
print(concatenate(('x', 'y', 'z'))) # 输出: xyz
Literal
Literal
是 Python typing
模块中的一个工具,用于指定特定的常量值。它允许你在类型提示中明确地指定某些值,从而增加代码的类型安全性和可读性。Literal
常用于函数参数、返回值和变量的类型注解中,以确保这些值只能是某些预定义的常量。
from typing import Literal
def set_mode(mode: Literal['auto', 'manual', 'off']) -> None:
print(f'{mode} mode has been set.')
set_mode('off')
set_mode('auto')
# 下面这行代码在类型检查时会报错,因为 "on" 不是允许的值
# set_mode("on")
from typing import Literal
def process_items(items: list[Literal['apple', 'banana', 'cherry']]) -> None:
for item in items:
print(item)
process_items(['apple', 'cherry', 'banana'])
# 下面这行代码在类型检查时会报错,因为 "orange" 不是允许的值
# process_items(["apple", "orange"])
Callable
Callable
用于类型提示中表示可调用对象(例如函数、方法或实现了__call__
方法的对象)
简单函数
from typing import Callable
# 定义一个接受函数作为参数的函数
def apply_function(func: Callable[[int, int], int], x: int, y: int) -> int:
return func(x, y)
# 定义符合类型提示的函数
def add(a: int, b: int) -> int:
return a + b
def multiply(a: int, b: int) -> int:
return a * b
# 调用 apply_function
print(apply_function(add, 2, 3)) # 输出: 5
print(apply_function(multiply, 2, 3)) # 输出: 6
带有任意参数的函数
from typing import Callable
# 定义一个接受带任意参数的函数
def call_with_args(func: Callable[..., int], *args, **kwargs) -> int:
return func(*args, **kwargs)
# 定义一些不同的函数
def sum_all(*args: int) -> int:
return sum(args)
def product_all(*args: int) -> int:
result = 1
for num in args:
result *= num
return result
# 调用 call_with_args
print(call_with_args(sum_all, 1, 2, 3, 4, 5)) # 输出: 15
print(call_with_args(product_all, 1, 2, 3, 4, 5)) # 输出: 120
类中的可调用对象
可以使用Callable
来表示类中实现了__call__
方法的对象:
使用class去实例化一个对象时,并不会触发__call__
方法。实例化后的对象名加括号才会调用__call__
方法
from typing import Callable
class Greeter:
def __call__(self, name: str) -> str:
return f"Hello, {name}!"
def greet(greeter: Callable[[str], str], name: str) -> str:
return greeter(name)
# 创建 Greeter 实例
greeter = Greeter()
# 调用 greet 函数
print(greet(greeter, "Alice")) # 输出: Hello, Alice!
NewType
用于创建简单的类型别名。它允许你定义一个新的类型,它在运行时与原始类型相同,但在类型检查时被视为不同的类型。
复杂的组合的类型提示
复杂的类型可以赋值给一个变量,相当于一个类型的别名
from typing import Literal
# 相当于用一个变量来代替一个复杂的类型
my_type = dict[str, list[Literal['apple','banana','cherry','orange','pair','tomato', 'liulian']]]
def print_dict(dic: my_type) -> None:
for k, v in dic.items():
print(k, v)
real_dic: my_type = {'roland': ['cherry', 'orange'], 'harry': ['banana', 'liulian'], 'cong': ['tomato', 'liulian']}
print_dict(real_dic)
类型别名的问题
不加类型控制,id和name传错时不会有任何提示
class Student:
def __init__(self, id: str, name: str):
self.user_id: str = id
self.user_name: str = name
id: str = 'A0001' # 这个是名字,却传给了id
name: str = 'harry'
stu = Student(name, id)
print(stu.user_id, stu.user_name)
直接将类型赋值给变量,则会将变量和相关的类型视为完全等价
uid = str
uname = str
class Student:
def __init__(self, id: uid, name: uname):
self.user_id: uid = id
self.user_name: uname = name
id: uid = 'harry'
name: uname = 'A0001'
# name已经声明为uname类型了,但是传参时还是放在了应该为uid的位置,但mypy并不会提示错误。 因为现在uid, uname, str都是等价的了
stu = Student(name, id)
print(stu.user_id, stu.user_name)
# 直接传常量值并不会报错
stu = Student('harry', 'A0001')
print(stu.user_id, stu.user_name)
NewType
用法
为了显示区分同种类型,比如str,使用NewType
from typing import NewType
# uid和uname会被视为各自不同的新类型,且与str也完全不同
uid = NewType('uid', str)
uname = NewType('uname', str)
class Student:
def __init__(self, id: uid, name: uname):
self.user_id: uid = id
self.user_name: uname = name
id: uid = uid('A0001')
name: uname = uname('harry')
# 这时参数传错,mypy就会提示错误
stu = Student(name, id)
print(stu.user_id, stu.user_name)
# 传常量值时mypy也会提示错误,因为mypy认为uid, uname, str是各不相同的类型
stu = Student('harry', 'A0001')
print(stu.user_id, stu.user_name)
# 若需要传常量, 则应使用如下写法,这样mypy不会提示类型错误
stu = Student(uid('A0001'), uname('harry'))
print(stu.user_id, stu.user_name)
泛型
泛型允许你定义数据类型的参数化类或函数,以提高代码的复用性和类型安全性。
泛型函数
from typing import TypeVar
T = TypeVar('T', int, str)
# T = TypeVar('T') 不对类型变量进行约束的话,mypy会对下面函数的定义有错误提示 error: Unsupported left operand type for + ("T") [operator]
def a_plus_b(a: T, b: T) -> T:
return a + b
ret = a_plus_b(1,2)
print(ret)
ret2 = a_plus_b('1','2')
print(ret)
def first_element(lst: list[T]) -> T:
return lst[0]
# 调用泛型函数
print(first_element([1, 2, 3])) # 输出: 1
print(first_element(["a", "b", "c"])) # 输出: a
泛型类
from typing import TypeVar, Generic
# 定义一个类型变量T
T = TypeVar('T')
class Box(Generic[T]):
def __init__(self, first_element: T, second_element: T):
self.first_element = first_element
self.second_element = second_element
def get_first(self) -> T:
return self.first_element
print(Box(1,2).get_first())
print(Box(1,'2').get_first()) # mypy虽然没提示有问题,但是pycharm编辑器却提示了问题
多个类型变量的用法
from typing import TypeVar, Generic
# 定义两个类型变量T和U
T = TypeVar('T')
U = TypeVar('U')
# 定义一个泛型类
class Pair(Generic[T, U]):
def __init__(self, first: T, second: U):
self.first = first
self.second = second
def get_first(self) -> T:
return self.first
def get_second(self) -> U:
return self.second
# 创建Pair的实例
pair = Pair(123, "hello")
# 调用方法
print(pair.get_first()) # 输出: 123
print(pair.get_second()) # 输出: hello
Annotated
未完待续
标签:return,name,self,模块,str,Typing,print,def From: https://www.cnblogs.com/rolandhe/p/18383650