Typing模块

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

未完待续

posted @   RolandHe  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示