# 数据类型

# 内置类

  • Python 提供了 int、str 等基本数据类型。每种数据类型(或者说数据结构),实际上由某个内置 class 定义。
    • 例:
      >>> type(1)
      <class 'int'>
      >>> int         # 在 C 语言中,int 是一个关键字。而在 Python 中,int 是一个 class 类名
      <class 'int'>
      
    • 调用某种数据类型的类名,可以创建这种数据类型的对象:
      >>> int()
      0
      >>> str()
      ''
      
    • 调用某种数据类型的类名,并输入一个值,可以将值转换成这种数据类型:
      >>> int('1')
      1
      >>> str(1)
      '1'
      
  • 用户也可以自己创建 class ,自定义一些数据类型,比如三角函数。

# 不可变类型

  • Python 的以下数据类型,属于可变类型,允许读取、修改:

    list
    set
    dict
    

    它们的共同特点:

    • 可以增加元素,也可以移除元素。
    • 可以调用 clear() 方法,删除所有元素。
  • Python 的以下数据类型,属于不可变类型,只允许读取,不允许修改:

    int
    float
    complex
    tuple
    str
    bytes
    
    • 例:
      >>> a = 'hello'
      >>> a[0] = 'H'   # str 对象属于不可变类型,修改它会导致报错
      TypeError: 'str' object does not support item assignment
      >>> a = 'world'  # 这并不是修改 str 对象 ,而是让变量 a 指向另一个对象
      >>> a = a + '!'  # 这并不是修改 str 对象,而是拼接两个字符串,生成一个新 str 对象
      
    • tuple 属于不可变类型,每个索引相当于一个 Python 变量,只能指向固定某个对象,不能换成其它对象。但如果该对象属于可变类型,则可以被修改。
      >>> a = (1, 2, [])
      >>> a[2] = [3]      # 不能改变某个索引指向的对象
      TypeError: 'tuple' object does not support item assignment
      >>> a[2].append(3)  # 可以修改该对象本身
      >>> a
      (1, 2, [3])
      

# bool

  • bool 类型的对象,只有 TrueFalse 两种取值(首字母必须大写),用于在逻辑判断中表示真、假。

    • bool 汉语译为"布尔"。
  • 在 Python 解释器的底层,所有对象都以二进制的形式存储。其中,True 被存储为数字 1 ,False 被存储为数字 0 。

    • 因此,bool 值,可以与数字混合运算:
      >>> True - 1
      0
      >>> True + 1
      2
      >>> True + False
      1
      >>> True > False
      True
      
  • 通过 bool(<object>) 可以将任意类型的对象,转换成 bool 值。

    • 大部分对象,会被转换成 True 。
      >>> bool(-1)      # -1 虽然为负数,但取值不为空,因此被视作 True
      True
      >>> bool('0')     # '0' 是一个 str 类型对象,Python 不管这个字符串的内容是什么,只要字符串的长度大于 0 ,则视作 True
      True
      >>> bool('False')
      True
      
    • 以下对象,会被转换成 False 。
      >>> bool(1 > 2)                                           # 结果为假的逻辑表达式
      False
      >>> bool(None), bool(''), bool(()), bool([]), bool({})    # 取值为空的基本数据类型对象
      (False, False, False, False, False)
      >>> bool(0), bool(0.0), bool(-0), bool(-0.0)              # 正数 0 和负数 0
      (False, False, False, False)
      

# 数字

# int

  • int 类型的对象,用于存储整数。

    • int 是英文单词 integer 的缩写,汉语译为"整数"、"整型"。
    • 例:
      >>> type(1)
      <class 'int'>
      
  • 当用户输入一个整数常量时,默认会被 Python 解释器视作十进制数字。

    • 也可以给数字添加以下前缀,声明为其它进制:
      0b    # 二进制
      0o    # 八进制
      0x    # 十六进制
      
    • 例:
      >>> 10          # 整型常量,默认被视作十进制
      10
      >>> 01          # 十进制数字,不能以 0 开头
        File "<stdin>", line 1
          01
          ^
      SyntaxError: leading zeros in decimal integer literals are not permitted; use an 0o prefix for octal integers
      >>> 0b10        # 将一个数字,声明为二进制。会被 Python 解释器自动转换成十进制,然后存储成 int 型对象
      2               # 打印时,依然采用十进制的形式
      >>> 0o10
      8
      >>> 0x10
      16
      
  • 从其它进制转换成十进制:

    >>> int('10', base=8)     # 从 base 进制(str 类型),转换成十进制(int 类型)。base 默认为 10
    8
    >>> int('FF', base=16)
    255
    >>> int('0xFF', base=16)  # 可以添加 0x 等进制前缀
    255
    
  • 从十进制转换成其它进制:

    >>> bin(255)              # 从十进制(int 类型)转换成二进制(str 类型)
    '0b11111111'
    
    >>> oct(8)                # 从十进制(int 类型)转换成八进制(str 类型)
    '0o10'
    
    >>> hex(16)               # 从十进制(int 类型)转换成十六进制(str 类型)
    '0x10'
    >>> '{:02x}'.format(16)   # 也可以用 str.format() 方法转换,这样没有 0x 前缀
    '10'
    

# float

  • C 语言中,设计了 double、float 两种浮点数(即包含小数的数字)。而 Python 中,只设计了 float 这种浮点数。

    • 例:
      >>> type(3.14)
      <class 'float'>
      
    • float 类型至少会保留一位小数,因此与 int 类型看起来明显不同:
      >>> 0.000
      0.0
      
    • float 默认最多保留 16 位小数,因此不能保存很长的小数:
      >>> 5/3                   # 5/3 的结果是一个无限循环小数,但 Python 只能保留有限长度的小数部分
      1.6666666666666667
      >>> round(5/3, 3)         # 用 round() 函数,可以四舍五入,只保留指定长度的小数部分
      0.333
      >>> format(5/3, '.3f')    # 将 float 值打印成字符串时,可以只保留 n 位小数
      '1.667'
      >>> format(5/3, '.20f')   # 如果打印超过 16 位的小数,则从第 17 位开始误差很大,没用
      '1.66666666666666674068'
      
  • float 类型的常量,可以用字母 e 声明为科学计数法。

    >>> 1.2e+3
    1200.0
    >>> 1.2e-3
    0.0012
    
  • 几个特殊的浮点数:

    >>> float('+inf')   # 正无穷大
    inf
    >>> float('-inf')   # 负无穷大
    -inf
    >>> float('nan')    # 非数字
    nan
    

# complex

  • complex 类型的对象,用于存储复数。
    • 复数的实部、虚部都是 float 浮点数。
    • 复数的虚部,加上字母 j 后缀。
    • 例:
      >>> 2j
      2j
      >>> 1+2j
      (1+2j)
      >>> complex(1, 2) # 用 complex() 创建一个复数
      (1+2j)
      >>> complex(1)    # 没有输入虚部时,默认值为 0j
      (1+0j)
      

# 序列

  • Python 中,list、tuple、str、bytes 等类型,都属于序列(sequence)。因为它们都会按顺序存放一组元素。

    • list、tuple 中,每个元素,可以是任意类型的对象。
    • str 中,每个元素,是一个字符。
    • bytes 中,每个元素,是一个十六进制值。
  • 序列类型的对象,具有以下特点:

    • 每个元素有一个不同的序号,又称为索引(index)。
      • 索引从 0 开始递增。第一个元素的索引为 0 ,第二个元素的索引为 1 ,以此类推,第 n 个元素的索引为 n-1 。
    • 可用索引的语法。
    • 可用切片的语法。
    • 可用加号 + 拼接两个序列。例:
      >>> [1, 2, 3] + [4, 5, 6]
      [1, 2, 3, 4, 5, 6]
      >>> [1, 2, 3] + 'hello'   # 不同数据类型之间,不支持拼接
      TypeError: can only concatenate list (not "str") to list
      >>> {1} + {2}             # set 类型不属于序列,不支持用加号拼接
      TypeError: unsupported operand type(s) for +: 'set' and 'set'
      >>> dict() + dict()       # dict 类型不属于序列,不支持用加号拼接
      TypeError: unsupported operand type(s) for +: 'dict' and 'dict'
      
    • 可用乘号 * 将一个序列复制几份,然后拼接在一起。例:
      >>> [1, 2, 3] * 2
      [1, 2, 3, 1, 2, 3]
      >>> 'hello' * 2
      'hellohello'
      

# 索引

  • 可用 sequence[下标] 的语法,获取序列中的一个元素(要求它的序号与下标匹配)。

    • 例:
      >>> a = [1, 2, 3]
      >>> a[0]    # 下标为 0 ,指向第一个元素
      1
      >>> a[-0]   # [-0] 的效果相当于 [0]
      1
      
    • 下标可以为负数,表示倒序索引,[-n] 指向倒数第 n 个元素。
      >>> a[-1]
      3
      >>> a[-2]
      2
      
    • 下标不能超过序列的长度,即超过 len(sequence) ,否则会抛出异常。
      >>> a[3]
      IndexError: list index out of range
      >>> a[-4]
      IndexError: list index out of range
      
      因此,下标的有效取值范围为 -len ≤ 下标 ≤ len-1
  • 上例中,通过下标获取一个元素之后,只是读取该元素的值。下面考虑修改的情况。

    • 如果序列为 tuple、str、bytes 类型,则属于不可变类型,不允许修改元素。
      >>> a = (1, 2, 3)
      >>> a[0] = 0
      TypeError: 'tuple' object does not support item assignment
      
    • 如果序列为 list 类型,则可以给该元素赋值:
      >>> a = [1, 2, 3]
      >>> a[0] = 0
      >>> a     # 可见 list 被修改了
      [0, 2, 3]
      
      还可以用关键字 del 进行删除:
      >>> del a[0]
      >>> a
      [2, 3]
      

# 切片

  • 可用 sequence[start:stop:step] 的语法,获取序列中 start ≤ index < stop 范围内的一连串元素,称为一个切片(slice)。

    • 这三种下标都有默认值,可以不填。
      • start 默认值为 0 。
      • stop 默认值为 len(sequence) 。
      • step 默认值为 1 。
    • 例:
      >>> a = [1, 2, 3]
      >>> a[:]
      [1, 2, 3]
      >>> a[0:]
      [1, 2, 3]
      >>> a[0:0]  # 这是获取 0 ≤ index < 0 范围内的元素,但不存在这样的元素,因此返回一个空的 list
      []
      >>> a[0:1]
      [1]
      >>> a[0:2]
      [1, 2]
      >>> a[0:-1] # 输入的下标,可以为负数
      [1, 2]
      
  • 切片中,下标可以填入任意 int 数字,不会引发异常。

    • stop 可以超过序列的长度:
      >>> a = [1, 2, 3]
      >>> a[0:9]  # 这是获取 0 ≤ index < 9 范围内的元素
      [1, 2, 3]
      
    • start 可以大于 stop:
      >>> a[9:0]  # 这是获取 9 ≤ index < 0 范围内的元素,但不存在这样的元素,因此返回一个空的 list
      []
      
  • step 称为步进值。作用是:从 start 这个元素开始,每隔 step 个元素,获取一个元素。

    • 例:
      >>> a = [1, 2, 3, 4, 5]
      >>> a[::2]
      [1, 3, 5]
      >>> a[1::2]
      [2, 4]
      
    • step 可以为负数,表示从右往左,倒序获取元素。
      >>> a[::-1]
      [5, 4, 3, 2, 1]
      >>> a[::-2]
      [5, 3, 1]
      
  • 如果序列为 list 类型,则可以给切片赋值:

    >>> a = [1, 2, 3, 4, 5]
    >>> a[0:2]
    [1, 2]
    >>> a[0:2] = [0]  # 赋值时,元素个数不必一致
    >>> a             # 可见 list 被修改了
    [0, 3, 4, 5]
    

    还可以用关键字 del 进行删除:

    >>> del a[0:2]
    >>> a
    [4, 5]
    >>> del a[:]      # 这是删除所有元素
    >>> a
    []
    >>> a[0:2] = []   # 赋值为空时,相当于删除这部分元素
    
  • 可以将切片保存为一个 slice 对象,然后赋值给一个名称有意义的变量,从而提升代码的可读性。

    >>> slice(0, 1)
    slice(0, 1, None)
    >>> first_element = _
    >>> a = [1, 2, 3, 4, 5]
    >>> a[first_element]
    [1]
    

# list

# tuple

  • tuple 类型,与 list 类似,也是有序地存放一组元素。

    • tuple 汉语译为"元组"。
  • 对比 list 与 tuple 类型:

    • tuple 只允许读,不允许修改。
    • tuple 只能调用 tuple.count() 等少量方法。不支持 list 的大部分方法,因为 tuple 是只读的。
  • 如何创建一个 tuple 对象?

    • 可以调用 tuple() ,用法与 list() 差不多。
      • 例:
        >>> tuple()         # 输入参数为空时,会创建一个空的 tuple 对象,不包含任何元素
        []
        >>> tuple('hello')  # 输入一个可迭代对象时,会遍历其中的元素,组成一个 tuple 对象
        ('h', 'e', 'l', 'l', 'o')
        
    • 用户可以输入一组元素,用英文逗号分隔,用圆括号 ( ) 作为定界符。此时 Python 解释器会将它保存为 tuple 对象。
      • 例:
        >>> (1, 2, 3)
        (1, 2, 3)
        >>> type(_)
        <class 'tuple'>
        
      • 如果用户输入一组元素,用英文逗号分隔,但没有加定界符。此时 Python 解释器会将它保存为 tuple 对象,而不是 list 对象。
        >>> 1, 2, 3
        (1, 2, 3)
        
      • 如果 tuple 只包含一个元素,则必须加上逗号,强调为 tuple 类型。
        >>> ()    # 这是一个空 tuple
        ()
        >>> (1)   # 这个 tuple 只包含一个元素,此时 Python 解释器会自动省略两侧的圆括号
        1
        >>> (1,)  # 加上逗号,强调为 tuple 类型
        (1,)
        
    • 对于 list ,Python 提供了列表推导式的语法,但对于 tuple ,没有元组推导式的语法。
      • 如果将列表推导式的定界符改为圆括号 ( ) ,则会创建一个生成器,而不是一个元组。
        >>> (i for i in range(3))
        <generator object <genexpr> at 0x0000029780FE8EB0>
        >>> tuple(_)  # 可以手动将生成器,转换成 tuple 类型
        (0, 1, 2)
        

# str

# bytes

  • bytes 表示字节类型,用于存储一段二进制数据,或者说 n 个字节的数据。

    • bytes 与 str 类似,兼容 str 的大部分方法。
    • str 以 Unicode 字符为单位,存储数据。
    • bytes 以 byte 字节为单位,存储数据。
  • 在 Python 解释器的底层,所有对象都以二进制的形式存储。当用户处理这些二进制数据时,就需要使用 bytes 类型的对象。

    • 例如一个字符串 str ,是存储为一段二进制数据。
    • 例如一个图片文件,是存储为一段二进制数据。
    • 例如一个 mp3 文件,是存储为一段二进制数据。
  • 如何创建一个 bytes 对象?

    • 可以调用 bytes()
      >>> bytes()               # 输入参数为空时,是创建一个长度为 0 的 bytes 对象
      b''
      >>> bytes(5)              # 输入整数,会创建一个指定长度的 bytes 对象,由空字节组成
      b'\x00\x00\x00\x00\x00'
      
    • 可以给一个字符串,添加 b 前缀,将它声明为 bytes 对象。
      >>> b'Hello'
      b'Hello'
      >>> type(_)
      <class 'bytes'>
      
    • 可以调用 str.encode() ,将 str 对象转换成 bytes 对象:
      >>> 'Hello'.encode()
      b'Hello'
      
  • 对 bytes 对象的索引:

    >>> b ='你好'.encode('gbk')
    >>> b
    b'\xc4\xe3\xba\xc3'
    >>> b[0]          # 索引 bytes 对象中的某个 byte 时,会自动将这个 byte 从十六进制,转换成十进制数
    196
    >>> hex(b[0])
    '0xc4'
    >>> b[0:1]        # 索引 bytes 对象中的一个切片时,会返回一个新的 bytes 对象
    b'\xc4'
    

# bytearray

  • bytearray 相当于将 bytes 以 list 的形式存储,允许修改。
    • 例:创建 bytearray
      >>> bytearray(3)                # 可以输入一个数字,这会创建一个指定长度的 bytearray
      bytearray(b'\x00\x00\x00')
      >>> bytearray('hello')          # 不能输入 str 类型的对象
      TypeError: string argument without an encoding
      >>> bytearray('hello'.encode()) # 可以输入 bytes 类型的对象,这会转换成 bytearray 类型
      bytearray(b'hello')
      
    • 例:修改 bytearray
      >>> b = bytearray('hello'.encode())
      >>> del b[0]
      >>> b
      bytearray(b'ello')
      

# 哈希表

  • 在 Python 底层,
    • list 类型的对象,存放所有元素时,采用的数据结构为数组。
      • 查找、附加一个元素时,时间复杂度为 O(1) 。
      • list.pop()list.append() 的时间复杂度为 O(n) 。因为每删除、插入一个元素,都要移动后面所有元素的位置,改变它们的索引。
    • set、dict 类型的对象,存放所有元素时,采用的数据结构为哈希表。根据每个元素的哈希值,确定该元素的存储地址。
      • 查找、删除、插入一个元素,时间复杂度为 O(1) ,效率高。
      • 为了保证每个元素能计算出哈希值,set 要求每个元素属于不可变类型,dict 要求每个元素的 key 属于不可变类型。
      • 为了保证每个元素能计算出不同的哈希值,set 要求每个元素不能重复,dict 要求每个元素的 key 不能重复。

# set

# dict

# 特殊值

# None

  • None 在 Python 中是一个特殊的值,表示取值为空。
    >>> None            # 在 Python 终端,如果一行代码的返回值为 None ,则不会显示该值
    >>> print(None)     # 如果主动用 print() 函数打印,则会显示 None 值
    None
    >>> type(None)      # 属于 NoneType 类型
    <class 'NoneType'>
    >>> None == 0       # 空值并不等于 0
    False
    

# pass

  • pass 在 Python 中是一个关键字,表示不执行任何操作。

    • Python 要求每个语句块至少包含一行代码。因此如果一个语句块的内容为空,可用 pass 填补空缺。
    • 例:
      >>> def fun1():
      ...     pass
      ...
      >>> fun1()
      
  • 省略号 ... 在 Python 中是一个从 ellipsis 类创建的单例对象,表示省略一处内容。

    • 例:
      >>> ...
      Ellipsis
      >>> type(...)
      <class 'ellipsis'>
      >>> bool(...)   # 它的布尔值为 True
      True
      
    • ... 可以像 pass 一样,填补空缺的语句块。
      >>> def fun1():
      ...     ...
      ...
      >>> fun1()
      
    • ... 可用于赋值。
      >>> a = ...
      >>> a = pass    # pass 是一个关键字,不能用于赋值
      SyntaxError: invalid syntax
      
    • 当对象中的某个元素是对自身的循环引用时,会显示为 ...
      >>> a = [1, 2, 3]
      >>> a.append(a)
      >>> a
      [1, 2, 3, [...]]
      >>> a[3]
      [1, 2, 3, [...]]
      >>> a[3][3]     # 列表 a 循环引用自己,形成了无限循环
      [1, 2, 3, [...]]
      
    • 为了避免循环引用,应该避免引用传递,改用值传递:
      >>> a = [1, 2, 3]
      >>> a.append(a.copy())  # 通过浅拷贝,实现值传递
      >>> a
      [1, 2, 3, [1, 2, 3]]
      
      >>> a = [1, 2, 3]
      >>> a = a + [a]         # 这也属于值传递
      >>> a
      [1, 2, 3, [1, 2, 3]]