数据

数据型决定了一个对象是否可以执行某些操作,或者某些操作是否根本不合理。其他编程语言通常通过确保对象永远不会存储在会对其执行操作的地方来确定一个操作是否适用于一个对象(这种型系统称为静态型)。Python 不这样做。相反,它将对象的型与对象本身一起存储,并在执行操作时检查该操作是否适用于该对象(这称为动态型)。

内置数据

Python 的内置(或标准)数据型可以分为几。按照 Python 官方文档中使用的层次结构,这些数据型包括:数值型、序列、集合和映射(以及这里没有进一步讨论的其他型)。以下是一些常见的内置数据型:

  • 布尔:表示内置值 True 和 False。在条件表达式中以及任何需要表示某些条件真假值的地方都很有用。与整数 1 和 0 大多数情况下可以互换。事实上,条件表达式接受任何型的值,将布尔值 False、整数 0 和空字符串 "" 视为 False,而其他所有值视为 True。

数值型:

  • int:整数;在 Python 2.x 中等价于 C 中的 long,Python 3.x 中长度不受限制。
  • long:非限制长度的长整数;仅在 Python 2.x 中存在。
  • float:浮点数,等价于 C 中的 double。
  • complex:复数。

序列型:

  • str:字符串;在 Python 2.x 中表示为一系列 8 位字符,在 Python 3.x 中表示为一系列 Unicode 字符(范围为 U+0000 - U+10FFFF)。
  • bytes:整数序列,范围为 0-255;仅在 Python 3.x 中可用。
  • bytearray似于 bytes,但可变;仅在 Python 3.x 中可用。
  • list:列表。
  • tuple:元组。

集合型:

  • set:无序的唯一元素集合;自 Python 2.6 起作为标准型可用。
  • frozenset:与 set 似,但不可变;自 Python 2.6 起作为标准型可用。

映射型:

  • dict:Python 字典,也叫哈希映射或关联数组,意味着列表中的一个元素与一个定义相关联,似于 Java 中的 Map。

可变与不可变对象

一般来说,Python 中的数据型可以根据其对象是可变的还是不可变的来区分。不可变型的对象在创建后不能更改其内容。

一些不可变型:

  • intfloatcomplex
  • str
  • bytes
  • tuple
  • frozenset
  • bool

一些可变型:

  • array
  • bytearray
  • list
  • set
  • dict

只有可变对象才支持在原地改变对象的方法,例如重新赋值序列切片,这对列表有效,但对元组和字符串则会引发错误

理解这一点非常重要,因为在 Python 中,变量实际上只是内存中对象的引用。如果你将一个对象赋值给变量,像这样:

a = 1
s = 'abc'
l = ['a string', 456, ('a', 'tuple', 'inside', 'a', 'list')]

你实际上是在让变量(a、s 或 l)指向内存中的对象(1、'abc' 或 ['a string', 456, ('a', 'tuple', 'inside', 'a', 'list')]),这个对象存储在内存的某个地方,以便方便访问。如果你重新赋值一个变量,像这样:

a = 7
s = 'xyz'
l = ['a simpler list', 99, 10]

你让变量指向一个不同的对象(在我们的例子中是新创建的对象)。如前所述,只有可变对象可以在原地修改(例如 l[0] = 1 在我们的例子中是可以的,但 s[0] = 'a' 会引发错误)。当操作没有显式要求在原地进行修改时,就会变得复杂,例如 +=(增量)运算符。当用于不可变对象时(如 a += 1s += 'qwertz'),Python 会悄无声息地创建一个新对象,并让变量指向它。然而,当用于可变对象时(如 l += [1,2,3]),变量指向的对象会在原地被修改。虽然在大多数情况下,你不需要知道这种不同的行为,但当多个变量指向同一个对象时,这种行为变得很重要。在我们的例子中,假设你设置 p = sm = l,然后执行 s += 'etc'l += [9,8,7],这会改变 s,但不会影响 p,但是会同时改变 ml,因为它们都指向同一个列表对象。Python 的内置函数 id(),返回给定变量名的唯一对象标识符,可以用来追踪发生了什么。

这种 Python 的行为在函数中通常会引起混淆。为了说明这一点,考虑以下代码:

def append_to_sequence(myseq):
    myseq += (9,9,9)
    return myseq

tuple1 = (1, 2, 3)  # 元组是不可变的
list1 = [1, 2, 3]   # 列表是可变的

tuple2 = append_to_sequence(tuple1)
list2 = append_to_sequence(list1)

print('tuple1 = ', tuple1)  # 输出 (1, 2, 3)
print('tuple2 = ', tuple2)  # 输出 (1, 2, 3, 9, 9, 9)
print('list1 = ', list1)    # 输出 [1, 2, 3, 9, 9, 9]
print('list2 = ', list2)    # 输出 [1, 2, 3, 9, 9, 9]

这将给出上述输出,通常是未预期的输出。myseqappend_to_sequence 函数的局部变量,但当调用此函数时,myseq 将指向我们传递的相同对象(在我们的例子中是 tuple1list1)。如果该对象是不可变的(如元组),就没有问题。+= 运算符将创建一个新元组,并将 myseq 设置为指向它。然而,如果我们传入一个可变对象的引用,该对象会在原地被修改(因此 myseqlist1 都最终指向同一个列表对象)。

链接:

  • 3.1. 对象、值和,Python 语言参考,docs.python.org
  • 5.6.4. 可变序列,Python 标准库,docs.python.org

创建定义型的对象

字面整数可以通过三种方式输入:

  • 十进制数字可以直接输入。
  • 十六进制数字可以通过在数字前加 0x 或 0X 来输入(例如 0xff 是十六进制的 FF,或者是十进制的 255)。
  • 八进制字面值的格式取决于 Python 的版本:
    • Python 2.x:八进制数字可以通过在数字前加零(0)来输入(例如 0732 是八进制的 732,或者是十进制的 474)。
    • Python 3.x:八进制数字可以通过在数字前加零和字母 O(0o 或 0O)来输入(例如 0o732 是八进制的 732,或者是十进制的 474)。
  • 浮点数可以直接输入。

长整数可以直接输入(例如 1234567891011121314151617181920 是一个长整数),或者通过在数字后加 L(例如 0L 是长整数)。涉及溢出的短整数计算会自动转换为长整数。

复数可以通过将一个实数和一个虚数相加来输入,虚数通过在数字后加 j 来表示(例如 10+5j 是复数,10j 也是复数)。注意,单独的 j 不构成数字。如果需要复数 1j,应该显式写出。

字符串可以是单引号或三重引号字符串。区别在于起始和结束定界符,以及单引号字符串不能跨越多行。单引号字符串通过输入单引号(')或双引号(")后跟匹配的引号来输入。因此:

'foo' 是有效的,
"moo" 也是有效的,

但:

'bar" 是无效的,
"baz' 也是无效的,
"quux'' 是错误的。

三重引号字符串似于单引号字符串,但可以跨越多行。它们的起始和结束定界符也必须匹配。三重引号可以通过连续的三个单引号或双引号输入,例如:

'''foo''' 是有效的,
"""moo""" 也是有效的,

但:

'"'bar'"' 是无效的,
"""baz''' 也是无效的,
'"'quux"'" 是错误的。

元组通过圆括号输入,元素之间用逗号分隔:

(10, 'Mary had a little lamb')

此外,在不产生歧义的情况下,可以省略括号:

10, 'whose fleece was as white as snow'

注意,单元素元组必须通过在元素周围加上括号并添加逗号来输入,例如:

('this is a singleton tuple',)

列表似,但使用方括号:

['abc', 1, 2, 3]

字典通过大括号包围键值对列表来创建,键和值通过冒号分隔,项之间用逗号分隔:

{ 'hello': 'world', 'weight': 'African or European?' }

这些复合型中的任何一个都可以包含其他型,深度不限:

((((((((('bob',), ['Mary', 'had', 'a', 'little', 'lamb']), { 'hello' : 'world' } ),),),),),),)

空对象

Python 中的空对象(似于其他编程语言中的空指针)是 NoneNone 不是空指针或空引用,而是一个实际的对象,且该对象只有一个实例。None 的一个常见用途是在函数的默认参数值中,详见 Python 编程/函数#默认_参数_值。与 None 的比较通常使用 is 而不是 ==

测试 None 和赋值:

if item is None:
  ...
  another = None

if not item is None:
  ...

if item is not None:  # 也可以使用
  ...

在默认参数值中使用 None

def log(message, type=None):
  ...

PEP8 规定:“与单例(如 None)的比较应该始终使用 isis not,绝不使用相等运算符。”因此,"if item == None:" 是不建议使用的。一个可以重新定义相等运算符==),使得它的实例等于 None

你可以通过 dir(None)id(None) 来验证 None 是一个对象。

参见运算符#身份章节。

链接:

型转换

Python 中的型转换示例:

v1 = int(2.7)  # 2
v2 = int(-3.9)  # -3
v3 = int("2")  # 2
v4 = int("11", 16)  # 17, 基数16
v5 = long(2)  # 仅在 Python 2.x 中,Python 3.x 不支持
v6 = float(2)  # 2.0
v7 = float("2.7")  # 2.7
v8 = float("2.7E-2")  # 0.027
v9 = float(False)  # 0.0
vA = float(True)  # 1.0
vB = str(4.5)  # "4.5"
vC = str([1, 3, 5])  # "[1, 3, 5]"
vD = bool(0)  # False; 从 Python 2.2.1 起支持 bool
vE = bool(3)  # True
vF = bool([])  # False - 空列表
vG = bool([False])  # True - 非空列表
vH = bool({})  # False - 空字典;空元组同样如此
vI = bool("")  # False - 空字符串
vJ = bool(" ")  # True - 非空字符串
vK = bool(None)  # False
vL = bool(len)  # True
vM = set([1, 2])
vN = set((1, 2))  # 可以转换任何序列,不仅仅是列表
vO = set("abc")  # {'c', 'b', 'a'}
vP = set(b"abc")  # {97, 98, 99}
vQ = list(vM)
vR = list({1: "a", 2: "b"})  # dict -> 键的列表
vS = tuple(vQ)
vT = list("abc")  # ['a', 'b', 'c']
print(v1, v2, v3, type(v1), type(v2), type(v3))

隐式型转换:

int1 = 4
float1 = int1 + 2.1  # 4 转换为浮点数
# str1 = "My int:" + int1  # 错误:无法从 int 到字符串进行隐式型转换
str1 = "My int:" + str(int1)
int2 = 4 + True  # 5: bool 被隐式转换为 int
float2 = 4.5 + True  # 5.5: True 被转换为 1,再转换为 1.0

关键词:型转换。

链接:

    1. 内置函数 # bool,docs.python.org
    1. 内置函数 # list,docs.python.org
    1. 内置函数 # float,docs.python.org
    1. 内置函数 # int,docs.python.org
    1. 内置函数 # set,docs.python.org
    1. 内置函数 # str,docs.python.org
    1. 内置函数 # type,docs.python.org
    1. 内置函数 # tuple,docs.python.org

练习

  1. 编写一个程序,实例化一个对象,向对象中添加 [1, 2],并返回结果。
  2. 找到一个对象,其返回的输出长度与输入长度相同(如果有的话?)。
  3. 找到一个对象,其返回的输出长度比开始时长 2。
  4. 找到一个会引发错误的对象。
  5. 找到两个数据型 X 和 Y,使得 X = X + Y 会引发错误,但 X += Y 不会引发错误
最后修改: 2025年01月30日 星期四 23:15