python typing模块的应用

1、介绍

Python是一门弱类型的语言,很多时候我们可能不清楚函数参数类型或者返回值类型,很有可能导致一些类型没有指定方法,在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果,就不得不去阅读代码的具体内容,降低了阅读的速度,typing模块可以很好的解决这个问题

自python3.5开始,PEP484为python引入了类型注解(type hints),typing模块的作用:

  • 类型检查,防止运行时出现参数和返回值类型不符合。
  • 作为开发文档附加说明,方便使用者调用时传入和返回参数类型。
  • 该模块加入后并不会影响程序的运行,不会报正式的错误,只有提醒pycharm目前支持typing检查,参数类型错误会黄色提示。

使用方式,函数接受并返回一个字符串,注释像下面这样:

def greeting(name: str) -> str:
    return 'Hello ' + name

在函数 greeting 中,参数 name 预期是 str 类型,并且返回 str 类型。子类型允许作为参数。

最新支持方式,请查看官方中文文档:https://docs.python.org/zh-cn/3.6/library/typing.html

2、基本应用

2.1、基本类型

  • int,long,float:整型,长整形,浮点型;
  • bool,str:布尔型,字符串类型;
  • List,Tuple,Dict,Set:列表,元组,字典,集合;
  • Iterable,Iterator:可迭代类型,迭代器类型;
  • Generator:生成器类型;
  • Any:它可以代表所有类型,所有的无参数类型注解都默认为Any 类型;
  • NoReturn,None:无返回值注解。
  • Sequence:是 collections.abc.Sequence 的泛型,不需要严格区分 list或tuple 类型时使用。

2.2、使用示例

1、使用or关键字或者Union表示多种类型。 List[int or str] <=> Union[int, str]。

from typing import List, Union

# 等价于  def func(a: int, string: str) -> Union[int, str] :
def func(a: int, string: str) -> List[int or str]: 
    list1 = []
    list1.append(a)
    list1.append(string)
    return list1
    
print(func(88,"999"))  

2、Any类型使用,是代码风格保持一致

def add(a):
    return a + 1
 # 等价于=>
def add(a: Any) -> Any:
    return a + 1

3、NoReturn,当一个方法没有返回结果时,为了注解它的返回类型,我们可以将其注解为 NoReturn。

def hello() -> NoReturn:
    print('hello')

3、高级应用

3.1、类型别名

类型别名通过将类型分配给别名来定义。在这个例子中, Vector 和 List[float] 将被视为可互换的同义词:

from typing import List
Vector = List[float]  # Vector 和 List[float] 将被视为可互换的同义词:

def scale(scalar: float, vector: Vector) -> Vector:
    return [scalar * num for num in vector]

类型别名可进行嵌套,用于简化复杂类型签名。例如:

from typing import Dict, Tuple, List

ConnectionOptions = Dict[str, str]
Address = Tuple[str, int]
Server = Tuple[Address, ConnectionOptions]

def broadcast_message(message: str, servers: List[Server]) -> None:
    ...

3.2、NewType

使用 NewType() 辅助函数创建不同的类型:

from typing import NewType

UserId = NewType('UserId', int)
some_id = UserId(524313)

静态类型检查器会将新类型视为它是原始类型的子类。这对于帮助捕捉逻辑错误非常有用:

def get_user_name(user_id: UserId) -> str:
    ...

# typechecks
user_a = get_user_name(UserId(42351))

# does not typecheck; an int is not a UserId
user_b = get_user_name(-1)

仍然可以对 UserId 类型的变量执行所有的 int 支持的操作,但结果将始终为 int 类型。这可以让你在需要 int 的地方传入 UserId,但会阻止你以无效的方式无意中创建 UserId:

# 'output' is of type 'int', not 'UserId'
output = UserId(23413) + UserId(54341)

3.3、Callable

Callable,可调用类型,它通常用来注解一个方法.Callable 在声明的时候需要使用 Callable[[Arg1Type, Arg2Type, …], ReturnType] 这样的类型注解,将参数类型和返回值类型都要注解出来.

def date(year: int, month: int, day: int) -> str:
    return f'{year}-{month}-{day}'
 
def get_date_fn() -> Callable[[int, int, int], str]:
    return date

3.4、用户定义的泛型类型

用户定义的类可以定义为泛型类。

from typing import TypeVar, Generic
from logging import Logger

T = TypeVar('T')

class LoggedVar(Generic[T]):
    def __init__(self, value: T, name: str, logger: Logger) -> None:
        self.name = name
        self.logger = logger
        self.value = value

    def set(self, new: T) -> None:
        self.log('Set ' + repr(self.value))
        self.value = new

    def get(self) -> T:
        self.log('Get ' + repr(self.value))
        return self.value

    def log(self, message: str) -> None:
        self.logger.info('%s: %s', self.name, message)

Generic[T] 作为基类定义了类 LoggedVar 采用单个类型参数 T。这也使得 T 作为类体内的一个类型有效。

The Generic base class uses a metaclass that defines __getitem__() so that LoggedVar[t] is valid as a type:

from typing import Iterable

def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
    for var in vars:
        var.set(0)

泛型类型可以有任意数量的类型变量,并且类型变量可能会受到限制:

from typing import TypeVar, Generic
...

T = TypeVar('T')
S = TypeVar('S', int, str)

class StrangePair(Generic[T, S]):
    ...

Generic 每个参数的类型变量必须是不同的。这是无效的:

from typing import TypeVar, Generic
...

T = TypeVar('T')

class Pair(Generic[T, T]):   # INVALID
    ...

可以对 Generic 使用多重继承:

from typing import TypeVar, Generic, Sized

T = TypeVar('T')

class LinkedList(Sized, Generic[T]):
    ...

从泛型类继承时,某些类型变量可能是固定的:

from typing import TypeVar, Mapping

T = TypeVar('T')

class MyDict(Mapping[str, T]):
    ...

在这种情况下,MyDict 只有一个参数,T。

在不指定类型参数的情况下使用泛型类别会为每个位置假设 Any。在下面的例子中,MyIterable 不是泛型,但是隐式继承自 Iterable[Any]:

from typing import Iterable

class MyIterable(Iterable): # Same as Iterable[Any]

用户定义的通用类型别名也受支持。例子:

from typing import TypeVar, Iterable, Tuple, Union
S = TypeVar('S')
Response = Union[Iterable[S], int]

# Return type here is same as Union[Iterable[str], int]
def response(query: str) -> Response[str]:
    ...

T = TypeVar('T', int, float, complex)
Vec = Iterable[Tuple[T, T]]

def inproduct(v: Vec[T]) -> T: # Same as Iterable[Tuple[T, T]]
    return sum(x*y for x, y in v)

The metaclass used by Generic is a subclass of abc.ABCMeta. A generic class can be an ABC by including abstract methods or properties, and generic classes can also have ABCs as base classes without a metaclass conflict. Generic metaclasses are not supported. The outcome of parameterizing generics is cached, and most types in the typing module are hashable and comparable for equality.

传送门:python系列文章目录及学习资源汇总

参考文章:

1、https://www.cnblogs.com/angelyan/p/11121859.html

2、https://blog.csdn.net/qq_21127151/article/details/104666542

 

你可能感兴趣的:(1.2,python包,python,pytorch,深度学习,typing,注解)