# 关于类型

# import collections

  • 除了 list、str 等基础数据类型,Python 还在 collections 这个标准库中,提供了一些额外的数据类型,用作存放数据的容器。
  • 官方文档 (opens new window)

# abc

  • collections.abc 提供了一些抽象类(abstract class),常用于类型注释、类型检查。

  • 如果一个对象实现了内置方法 __next__() ,则属于迭代器(Iterator)。

    >>> from collections.abc import Iterator
    >>> isinstance('Hello', Iterator)
    False
    >>> isinstance(iter('Hello'), Iterator)
    True
    
  • 如果一个对象实现了内置方法 __iter__() ,则属于可迭代对象(Iterable)。

    >>> from collections.abc import Iterable
    >>> isinstance('Hello', Iterable)
    True
    >>> isinstance(iter('Hello'), Iterable)
    True
    
  • 如果一个对象实现了内置方法 __hash__() ,则属于可哈希对象(Hashable)。

    >>> from collections.abc import Hashable
    >>> isinstance(1, Hashable)
    True
    

# Counter

  • 假设一个 list 包含一些重复的元素,如何统计每个元素的出现次数?

    • 可以对于每个元素,调用一次 list.count(xx) ,统计该元素的出现次数。但这样比较麻烦,性能低。
    • 可以基于 list 创建一个 collections.Counter 对象,自动统计每个元素的出现次数。
  • 定义:

    Counter(iterable=None, /, **kwargs)
    
    • Counter 类是 dict 类的子类,因此兼容 dict 的大部分方法。
    • 调用 Counter(iterable) 时,会将 iterable 对象中的每个元素,保存为字典中一个 key 。然后将每个元素的出现次数,保存为 key 对应的 value 。
  • 例:统计每个字母的出现次数

    >>> from collections import Counter
    >>> c = Counter('hello')
    >>> c
    Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1})
    >>> c['h']      # 查询某个元素的出现次数
    1
    >>> c['x']      # 如果查询的元素不存在,则返回 0 ,而不是抛出 KeyError 异常
    0
    >>> c['x'] = 1  # 可以修改元素的出现次数,像修改一个 dict 对象的 value
    >>> c.most_common(2)  # 返回出现次数最多的 n 个元素
    [('l', 2), ('h', 1)]
    >>> c.update('hi')    # 统计更多元素的出现次数,累加到当前 Counter 。这相当于 c += Counter('hi')
    >>> c
    Counter({'h': 2, 'l': 2, 'e': 1, 'o': 1, 'i': 1})
    
  • 两个 Counter 对象之间,支持以下运算符:

    >>> c1 = Counter('aab')
    >>> c1
    Counter({'a': 2, 'b': 1})
    >>> c2 = Counter('abb')
    >>> c2
    Counter({'b': 2, 'a': 1})
    >>> c1 & c2       # 交集
    Counter({'a': 1, 'b': 1})
    >>> c1 | c2       # 并集
    Counter({'a': 2, 'b': 2})
    >>> c1 - c2       # 差集
    Counter({'a': 1})
    >>> c1 + c2       # 累加
    Counter({'a': 3, 'b': 3})
    

# deque

  • list 类型没有长度限制,可以存放任意个元素。而 deque 类型,相当于一个限制了长度的 list 。

    • deque 缩写自 double-end queue(双端队列)。
    • deque 只能从左端或右端插入元素,不能从中间插入。因此时间复杂度为 O(1) ,性能高。
    • deque 从某一端插入一个元素时,如果 deque 已达到最大长度,则会从另一端删除一个元素。
  • 定义:

    deque(iterable=[], maxlen=None)
    
    • maxlen 默认值为 None ,表示不限制长度。
  • 例:创建

    >>> from collections import deque
    >>> deque()
    deque([])
    >>> deque(maxlen=3)
    deque([], maxlen=3)
    >>> deque('hello', maxlen=3)
    deque(['l', 'l', 'o'], maxlen=3)
    
  • 例:读取

    >>> q = deque('hello', maxlen=3)
    >>> q[0]    # 支持索引
    'l'
    >>> q[:]    # 不支持切片
    TypeError: sequence index must be integer, not 'slice'
    
  • 例:插入

    >>> q = deque('hello', maxlen=3)
    >>> q.append(4)       # 从右端插入元素
    >>> q
    deque(['l', 'o', 4], maxlen=3)
    >>> q.appendleft(5)   # 从左端插入元素
    >>> q
    deque([5, 'l', 'o'], maxlen=3)
    >>> q.extend('hi')
    >>> q
    deque(['o', 'h', 'i'], maxlen=3)
    >>> q.extendleft('hi')
    >>> q
    deque(['i', 'h', 'o'], maxlen=3)
    
  • 例:修改

    >>> q = deque('hello', maxlen=3)
    >>> q
    deque(['l', 'l', 'o'], maxlen=3)
    >>> q.reverse()   # 翻转列表,反向排序所有元素
    >>> q
    deque(['o', 'l', 'l'], maxlen=3)
    >>> q.rotate(1)   # 让所有元素向右移动 1 位,最右端的元素会插入左端
    >>> q
    deque(['l', 'o', 'l'], maxlen=3)
    >>> q.rotate(-1)  # 让所有元素向左移动 1 位
    >>> q
    deque(['o', 'l', 'l'], maxlen=3)
    
  • 例:删除

    >>> q = deque('hello', maxlen=3)
    >>> q.pop()
    'o'
    >>> q
    deque(['i', 'h'], maxlen=3)
    >>> q.popleft()
    'i'
    >>> q
    deque(['h'], maxlen=3)
    >>> q.clear()
    >>> q
    deque([], maxlen=3)
    

# import typing

:Python 的标准库,定义了一些数据类型,常用于类型注释、类型检查。

# 原理

  • 什么是类型注释?

    • Python 变量本身是无类型的,但可以添加类型注释,供 IDE 等工具进行静态类型检查。如下:
      >>> x: int = 1
      
    • 这里所说的类型,可以是 str、list 等内置类,或其它任何 class 。
    • 类型别名(Type Alias):指将一个类型赋值给一个变量。如下:
      >>> T = int
      >>> x : T = 1
      
  • 常见类型:

    Any         # 匹配所有类型
    
    List
    Tuple       # 元组。比如 Tuple[int, str] 表示取值为一个元组,包含两个指定类型的值。
    Sequence    # 序列
    Set
    Dict
    Iterator
    Iterable
    

# Callable

:表示可调用的对象。

  • 例:
    >>> from typing import Callable
    >>> x : Callable                    # 表示变量 x 是一个可调用对象
    >>> x : Callable[[int, int], str]   # 表示变量 x 可调用,且形参列表为 [int, int] 类型,返回值为 str 类型
    

# Optional

:表示取值是可选的。可以不赋值,即相当于赋值 None 。

  • 例:
    from typing import Optional
    def foo(arg: Optional[int] = None):
        pass
    

# Union

:表示属于多种类型之一,称为联合类型。

  • 定义 Union 类型时,
    • 如果只包含一个类型,则只返回该类型。
      >>> Union[int]
      <class 'int'>
      
    • 如果包含了重复的类型,则会去除重复:
      >>> Union[int, int]
      <class 'int'>
      
    • 如果嵌套了 Union 类型,则自动合并为一层 Union 。
      >>> Union[Union[int, str], float]
      typing.Union[int, str, float]
      
  • 比较两个 Union 类型时,不考虑参数的顺序。
    >>> Union[int, str] == Union[str, int]
    True
    >>> Union[int, str, int] == Union[str, int]
    True
    

# Type

:type 的别名。

  • 例:
    >>> from typing import Type
    >>> x : Type
    

# TypeVar

:用于定义类型变量,表示一种类型。

  • 例:
    >>> from typing import TypeVar
    >>> A = TypeVar('A')              # 定义一个类型变量 A ,可以赋值任意类型
    >>> A
    ~A
    >>> B = TypeVar('B', int, str)    # 定义一个类型变量 B ,只能赋值指定的类型
    >>> x : B = 1
    
  • TypeVar 变量可用作泛型的参数:
    >>> from typing import Sequence, TypeVar
    >>> T = TypeVar('T')
    >>> x : Sequence        # Sequence 是一个已定义的泛型
    >>> x : Sequence[T]
    >>> x : Sequence[T, T]  # Sequence 只支持声明一个参数
    TypeError: Too many arguments for typing.Sequence; actual 2, expected 1
    

# Generic

:用作泛型类的抽象基类。

  • 泛型类:表示一个任意类型的类,只声明了形参的类型、数量。
  • Generic 类的定义:
    class Generic:
        def __class_getitem__(cls, params):
            ...
    
        def __init_subclass__(cls, *args, **kwargs):
            ...
    
  • 例:定义泛型类
    >>> from typing import TypeVar, Generic
    >>> A = TypeVar('A')
    >>> B = TypeVar('B')
    >>> class Test(Generic):        # 继承 Generic 时,必须声明参数
    ...     pass
    ...
    TypeError: Cannot inherit from plain Generic
    >>> class Test(Generic[A]):
    ...     pass
    ...
    >>> class Test(Generic[A, A]):  # 如果声明多个参数,则必须互不相同
    ...     pass
    ...
    TypeError: Parameters to Generic[...] must all be unique
    >>> class Test(Generic[A, B]):
    ...     def __init__(self, x:A=None, y:B=None):
    ...         pass
    ...
    >>> Test()                # 创建泛型类的示例时,可以不声明参数,像普通类一样使用
    <__main__.Test object at 0x7f21e0369ff0>
    >>> Test[A]()             # 如果声明了参数,则会检查参数数量是否一致,否则抛出异常。但不会强制类型检查
      File "/usr/local/lib/python3.10/typing.py", line 1316, in __class_getitem__
        _check_generic(cls, params, len(cls.__parameters__))
    TypeError: Too few arguments for <class '__main__.Test'>; actual 1, expected 2
    >>> Test[int, int]()
    <__main__.Test object at 0x7f21e0368910>
    >>> Test[int, str](1, 2)  # 不会强制类型检查
    <__main__.Test object at 0x7f347beb5270>
    

# import string

:Python 的标准库,提供了一些常用的字符集合。

  • 官方文档 (opens new window)

  • 关于数字:

    >>> import string
    >>> string.digits         # 全部阿拉伯数字
    '0123456789'
    >>> string.hexdigits      # 全部十六进制数字
    '0123456789abcdefABCDEF'
    
  • 关于字母:

    >>> string.ascii_letters      # 全部 ASCII 字母
    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
    >>> string.ascii_lowercase    # 全部小写字母
    'abcdefghijklmnopqrstuvwxyz'
    >>> string.ascii_uppercase    # 全部大写字母
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    
  • 空白字符:

    >>> string.whitespace   # 所有空白字符
    ' \t\n\r\x0b\x0c'
    
    • \x0b 表示让光标垂直换行,等价于 \v
    • \x0c 表示让打印机换到下一张纸,等价于 \f
    • \b 表示让光标回退一格。
    • \x20 表示一个空格。