Wonder is the foundation of all philosophy, inquiry its
progress,ignorance its end. ——Michel de Montaigne 米歇尔·德·蒙田
怀疑是一切哲学的基础,探究是一切哲学的进步,无知是一切哲学的终结。
让我们暂时将第一份Python程序抛在脑后,来聊一聊数据类型。在 Python 中,每个值都有一种数据类型,但您并不需要声明变量的数据类型。那该方式是如何运作的呢?Python 根据每个变量的初始赋值情况分析其类型,并在内部对其进行跟踪。
Python有多种内置数据类型。以下是比较重要的一些:
当然,还有更多的类型。在 Python 中一切均为对象,因此存在像 module[模块]、 function[函数]、 class[类]、 method [方法]、 file[文件] 甚至 compiled code[已编译代码] 这样的类型。您已经见过这样一些例子:模块的 name、 函数的 docstrings 等等。将学到的包括 《类 与 迭代器》 中的 Classes [类],以及 《文件》 中的 Files[文件]。
Strings[字符串]和 Bytes[字节串]比较重要,也相对复杂,足以开辟独立章节予以描述,让我们先看看其他类型。
在布尔类型上下文中,您几乎可以使用任何表达式。
布尔类型或为真或为假。Python 有两个被巧妙地命名为 True 和 False 的常量,可用于对布尔类型的直接赋值。表达式也可以计算为布尔类型的值。在某些地方(如 if 语句),Python 所预期的就是一个可计算出布尔类型值的表达式。这些地方称为 布尔类型上下文环境。事实上,可在布尔类型上下文环境中使用任何表达式,而 Python 将试图判断其真值。在布尔类型上下文环境中,不同的数据类型对于何值为真、何值为假有着不同的规则。(看过本章稍后的实例后,这一点将更好理解。)
例如,看看 humansize.py 中的这个片段:
if size < 0:
raise ValueError('number must be non‐negative')
size 是整数, 0 是整数,而 < 是数字运算符。size < 0 表达式的结果始终是布尔值。可在 Python 交互式 shell 中自行测试下结果:
>>> size = 1
>>> size < 0
False
>>> size = 0
>>> size < 0
False
>>> size = ‐1
>>> size < 0
True
由于 Python 2 的一些遗留问题,布尔值可以当做数值对待。 True 为 1;False 为 0 。
>>> True + True
2
>>> True ‐ False
1
>>> True * False
0
>>> True / False
Traceback (most recent call last):
File "" , line 1, in <module> ZeroDivisionError: int division or modulo by zero
喔,喔,喔,别那么干!忘掉我刚才说的。
数值类型是可畏的。有太多类型可选了。Python 同时支持 Integer[整型] 和 Floating Point[浮点型] 数值。无任何类型声明可用于区分;Python 通过是否有小数点来分辨它们。
>>> type(1) ①
<class 'int'>
>>> isinstance(1, int) ②
True
>>> 1 + 1 ③
2
>>> 1 + 1.0 ④
2.0
>>> type(2.0)
<class 'float'>
正如刚才所看到的,一些运算符(如:加法)会根据需把整数强制转换为浮点数。也可自行对其进行强制转换。
>>> float(2) ①
2.0
>>> int(2.0) ②
2
>>> int(2.5) ③
2
>>> int(‐2.5) ④
-2
>>> 1.12345678901234567890 ⑤
1.1234567890123457
>>> type(1000000000000000) ⑥
<class 'int'>
Python 2 对于int[整型] 和 long[长整型] 采用不同的数据类型。int 数据类型受到 sys.maxint 的限制,因平台该限制也会有所不同,但通常是 232‐1 。Python 3 只有一种整数类型,其行为方式很有点像 Python 2 的旧 long[长整数] 类型。参阅 PEP 237 了解更多细节。
对数值可进行各种类型的运算
>>> 11 / 2 ①
5.5
>>> 11 // 2 ②
5
>>> −11 // 2 ③
-6
>>> 11.0 // 2 ④
5.0
>>> 11 ** 2 ⑤
121
>>> 11 % 2 ⑥
1
在 Python 2 中,运算符 / 通常表示整数除法,但是可以通过在代码中加入特殊指令,使其看起来像浮点除法。在 Python 3 中,/ 运算符总是表示浮点除法。参阅 PEP 238 了解更多细节。
Python 并不仅仅局限于整数和浮点数类型。它可以完成你在高中阶段学过、但几乎已经全部忘光的所有古怪数学运算。
>>> import fractions ①
>>> x = fractions.Fraction(1, 3) ②
>>> x
Fraction(1, 3)
>>> x * 2 ③
Fraction(2, 3)
>>> fractions.Fraction(6, 4) ④
Fraction(3, 2)
>>> fractions.Fraction(0, 0) ⑤
Traceback (most recent call last):
File "" , line 1, in <module>
File "D:\Python\lib\fractions.py", line 178, in __new__
raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
ZeroDivisionError: Fraction(0, 0)
还可在 Python 中进行基本的三角函数运算。
>>> import math
>>> math.pi ①
3.1415926535897931
>>> math.sin(math.pi / 2) ②
1.0
>>> math.tan(math.pi / 4) ③
0.99999999999999989
零值是 false[假],非零值是 true[真]。
可以在 if 这样的 布尔类型上下文环境中 使用数值。零值是 false[假],非零值是 true[真]。
>>> def is_it_true(anything): ①
... if anything:
... print("yes, it's true")
... else:
... print("no, it's false")
...
>>> is_it_true(1) ②
yes, it's true
>>> is_it_true(-1)
yes, it's true
>>> is_it_true(0)
no, it's false
>>> is_it_true(0.1) ③
yes, it's true
>>> is_it_true(0.0)
no, it's false
>>> import fractions
>>> is_it_true(fractions.Fraction(1, 2)) ④
yes, it's true
>>> is_it_true(fractions.Fraction(0, 1))
no, it's false
列表是 Python 的主力数据类型。当提到 “列表 ”时,您脑海中可能会闪现“必须进一步声明大小的数组,只能包含同一类对象”等想法。千万别这么想。列表比那要酷得多。
☞ Python 中的列表类似 Perl 5 中的数组。在 Perl 5中,存储数组的变量总是以字符 @ 开头;在 Python中,变量可随意命名,Python 仅在内部对数据类型进行跟踪。
☞ Python 中的列表更像 Java 中的数组(尽管可以把列表当做生命中所需要的一切来使用)。一个更好的比喻可能是 ArrayList 类,该类可以容纳任何对象,并可在添加新元素时进行动态拓展。
列表创建非常轻松:使用中括号包裹一系列以逗号分割的值即可。
>>> a_list = ['a', 'b', 'mpilgrim', 'z', 'example'] ①
>>> a_list
['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list[0] ②
'a'
>>> a_list[4] ③
'example'
>>> a_list[-1] ④
'example'
>>> a_list[-3] ⑤
'mpilgrim'
a_list[0] 是列表的第一个元素。
定义列表后,可从其中获取任何部分作为新列表。该技术称为对列表进行 切片 。
>>> a_list
['a', 'b', 'mpilgrim', 'z', 'example']
>>> a_list[1:3] ①
['b', 'mpilgrim']
>>> a_list[1:-1] ②
['b', 'mpilgrim', 'z']
>>> a_list[0:3] ③
['a', 'b', 'mpilgrim']
>>> a_list[:3] ④
['a', 'b', 'mpilgrim']
>>> a_list[3:] ⑤
['z', 'example']
>>> a_list[:] ⑥
['a', 'b', 'mpilgrim', 'z', 'example']
有四种方法可用于向列表中增加元素。
>>> a_list = ['a']
>>> a_list = a_list + [2.0, 3] ①
>>> a_list ②
['a', 2.0, 3]
>>> a_list.append(True) ③
>>> a_list
['a', 2.0, 3, True]
>>> a_list.extend(['four', 'Ω']) ④
>>> a_list
['a', 2.0, 3, True, 'four', 'Ω']
>>> a_list.insert(0, 'Ω') ⑤
>>> a_list
['Ω', 'a', 2.0, 3, True, 'four', 'Ω']
☞ a_list.insert(0, value) 就像是 Perl 中的unshift() 函数。它将一个元素添加到列表的头部,所有其它的元素都被顶离原先的位置以腾出空间。
让我们进一步看看 append() 和 extend() 的区别。
>>> a_list = ['a', 'b', 'c']
>>> a_list.extend(['d', 'e', 'f']) ①
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f']
>>> len(a_list) ②
6
>>> a_list[-1]
'f'
>>> a_list.append(['g', 'h', 'i']) ③
>>> a_list
['a', 'b', 'c', 'd', 'e', 'f', ['g', 'h', 'i']]
>>> len(a_list) ④
7
>>> a_list[-1]
['g', 'h', 'i']
>>> a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
>>> a_list.count('new') ①
2
>>> 'new' in a_list ②
True
>>> 'c' in a_list
False
>>> a_list.index('mpilgrim') ③
3
>>> a_list.index('new') ④
2
>>> a_list.index('c') ⑤
Traceback (most recent call last):
File "" , line 1, in <module>
ValueError: 'c' is not in list
等等,什么?是这样的:如果没有在列表中找到该值, index() 方法将会引发一个异常。这是 Python 语言最显著不同之处,其它多数语言将会返回一些无效的索引值(像是 ‐1)。当然,一开始这一点看起来比较讨厌,但我想您会逐渐欣赏它。这意味着您的程序将会在问题的源头处崩溃,而不是之后奇怪地、默默地崩溃。请记住, ‐1 是合法的列表索引值。如果 index() 方法返回 ‐1,可能会导致调整过程变得不那么有趣!
列表永远不会有缝隙。
列表可以自动拓展或者收缩。您已经看到了拓展部分。也有几种方法可从列表中删除元素。
>>> a_list = ['a', 'b', 'new', 'mpilgrim', 'new']
>>> a_list[1]
'b'
>>> del a_list[1] ①
>>> a_list
['a', 'new', 'mpilgrim', 'new']
>>> a_list[1] ②
'new'
不知道位置索引?这不成问题,您可以通过值而不是索引删除元素。
>>> a_list.remove('new') ①
>>> a_list
['a', 'mpilgrim', 'new']
>>> a_list.remove('new') ②
>>> a_list
['a', 'mpilgrim']
>>> a_list.remove('new')
Traceback (most recent call last):
File "" , line 1, in <module>
ValueError: list.remove(x): x not in list
REMOVING ITEMS FROM A LIST: BONUS ROUND,其实就是再举一个删除的例子。
另一有趣的列表方法是 pop() 。pop() 方法是从列表删除元素的另一方法,但有点变化。
>>> a_list = ['a', 'b', 'new', 'mpilgrim']
>>> a_list.pop() ①
'mpilgrim'
>>> a_list
['a', 'b', 'new']
>>> a_list.pop(1) ②
'b'
>>> a_list
['a', 'new']
>>> a_list.pop()
'new'
>>> a_list
['a']
>>> a_list.pop()
'a'
>>> a_list.pop() ③
Traceback (most recent call last):
File "" , line 1, in <module>
IndexError: pop from empty list
☞ 不带参数调用的 pop() 列表方法就像 Perl 中的pop() 函数。它从列表中删除最后一个元素并返回所删除元素的值。Perl 还有另一个函数 shift(),可用于删除第一个元素并返回其值;在 Python中,该函数相当于 a_list.pop(0) 。
空列表为假;其它所有列表为真。
可以在 if 这样的 布尔类型上下文环境中 使用列表。
>>> def is_it_true(anything):
... if anything:
... print("yes, it's true")
... else:
... print("no, it's false")
...
>>> is_it_true([]) ①
no, it's false
>>> is_it_true(['a']) ②
yes, it's true
>>> is_it_true([False]) ③
yes, it's true
元素是不可变的列表。一旦创建之后,用任何方法都不可以修改元素。
>>> a_tuple = ("a", "b", "mpilgrim", "z", "example") ①
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'example')
>>> a_tuple[0] ②
'a'
>>> a_tuple[‐1] ③
'example'
>>> a_tuple[1:3] ④
('b', 'mpilgrim')
元组和列表的主要区别是元组不能进行修改。用技术术语来说,元组是 不可变更 的。从实践的角度来说,没有可用于修改元组的方法。列表有像 append()、 extend()、 insert()、 remove() 和 pop() 这样的方法。这些方法,元组都没有。可以对元组进行切片操作(因为该方法创建一个新的元组),可以检查元组是否包含了特定的值(因为该操作不修改元组),还可以……就那么多了。
# continued from the previous example
>>> a_tuple
('a', 'b', 'mpilgrim', 'z', 'example')
>>> a_tuple.append("new") ①
Traceback (innermost last):
File "" , line 1, in ?AttributeError:
'tuple' object has no attribute 'append'
>>> a_tuple.remove("z") ②
Traceback (innermost last):
File "" , line 1, in ?AttributeError:
'tuple' object has no attribute 'remove'
>>> a_tuple.index("example") ③
4
>>> "z" in a_tuple ④
True
那么元组有什么好处呢?
☞ 元组可以转换为列表,反之亦然。内建的tuple()函数接收一个列表参数,并返回一个同样包含该元素的元组,而list()函数接收一个元组参数并返回一个列表。从效果上看,tuple()冻结列表,而list()融化元组。
可以在 if 这样的 布尔类型上下文环境中 使用元组。
>>> def is_it_true(anything):
... if anything:
... print("yes, it's true")
... else:
... print("no, it's false")
...
>>> is_it_true(()) ①
no, it's false
>>> is_it_true(('a', 'b')) ②
yes, it's true
>>> is_it_true((False,)) ③
yes, it's true
>>> type((False)) ④
<class 'bool'>
>>> type((False, ))
<class 'tuple'>
以下是一种很酷的编程捷径:在 Python 中,可使用元组来一次赋多值。
>>> v = ('a', 2, True) ①
>>> (x, y, z) = v
>>> x
'a'
>>> y
2
>>> z
True
该特性有多种用途。假设需要将某个名称指定某个特定范围的值。可以使用内建的 range() 函数进行多变量赋值以快速地进行连续变量赋值。
>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) ①
>>> MONDAY ②
0
>>> TUESDAY
1
>>> SUNDAY
6
还可以使用多变量赋值创建返回多值的函数,只需返回一个包含所有值的元组。调用者可将返回值视为一个简单的元组,或将其赋值给不同的变量。许多标准 Python 类库这么干,包括在下一章将学到的 os 模块。
集合 set 是装有独特值的无序“袋子”。一个简单的集合可以包含任何数据类型的值。如果有两个集合,则可以执行像联合、交集以及集合求差等标准集合运算。
重中之重。创建集合非常简单。
>>> a_set = {1} ①
>>> a_set
{1}
>>> type(a_set) ②
<class 'set'>
>>> a_set = {1, 2} ③
>>> a_set
{1, 2}
还可以以列表为基础创建集合。
>>> a_list = ['a', 'b', 'mpilgrim', True, False, 42]
>>> a_set = set(a_list) ①
>>> a_set ②
{'a', True, False, 'mpilgrim', 'b', 42}
>>> a_list ③
['a', 'b', 'mpilgrim', True, False, 42]
还没有任何值?没有问题。可以创建一个空的集合。
>>> a_set = set() ①
>>> a_set ②
set()
>>> type(a_set) ③
<class 'set'>
>>> len(a_set) ④
0
>>> not_sure = {} ⑤
>>> type(not_sure)
<class 'dict'>
有两种方法可向现有集合中添加值: add() 方法和 update() 方法。
>>> a_set = {1, 2}
>>> a_set.add(4) ①
>>> a_set
{1, 2, 4}
>>> len(a_set) ②
3
>>> a_set.add(1) ③
>>> a_set
{1, 2, 4}
>>> len(a_set) ④
3
>>> a_set = {1, 2, 3}
>>> a_set
{1, 2, 3}
>>> a_set.update({2, 4, 6}) ①
>>> a_set ②
{1, 2, 3, 4, 6}
>>> a_set.update({3, 6, 9}, {1, 2, 3, 5, 8, 13}) ③
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 13}
>>> a_set.update([10, 20, 30]) ④
>>> a_set
{1, 2, 3, 4, 5, 6, 8, 9, 10, 13, 20, 30}
有三种方法可以用来从集合中删除某个值。前两种,discard() 和 remove() 有细微的差异。
>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set
{1, 3, 36, 6, 10, 45, 15, 21, 28}
>>> a_set.discard(10) ①
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.discard(10) ②
>>> a_set
{1, 3, 36, 6, 45, 15, 21, 28}
>>> a_set.remove(21) ③
>>> a_set
{1, 3, 36, 6, 45, 15, 28}
>>> a_set.remove(21) ④
Traceback (most recent call last):
File "" , line 1, in <module>
KeyError: 21
就像列表,集合也有个 pop() 方法。
>>> a_set = {1, 3, 6, 10, 15, 21, 28, 36, 45}
>>> a_set.pop() ①
1
>>> a_set.pop()
3
>>> a_set.pop()
36
>>> a_set
{6, 10, 45, 15, 21, 28}
>>> a_set.clear() ②
>>> a_set
set()
>>> a_set.pop() ③
Traceback (most recent call last):
File "" , line 1, in <module>
KeyError: 'pop from an empty set'
Python 的 集合 类型支持几种常见的运算。
>>> a_set = {2, 4, 5, 9, 12, 21, 30, 51, 76, 127, 195}
>>> 30 in a_set ①
True
>>> 31 in a_set
False
>>> b_set = {1, 2, 3, 5, 6, 8, 9, 12, 15, 17, 18, 21}
>>> a_set.union(b_set) ②
{1, 2, 195, 4, 5, 3, 6, 8, 9, 12, 76, 15, 17, 18, 21, 30, 51, 127}
>>> a_set.intersection(b_set) ③
{2, 5, 9, 12, 21}
>>> a_set.difference(b_set) ④
{195, 4, 76, 51, 30, 127}
>>> a_set.symmetric_difference(b_set) ⑤
{1, 3, 195, 6, 4, 8, 76, 15, 17, 18, 51, 30, 127}
这三种方法是对称的。
# continued from the previous example
>>> a_set.symmetric_difference(b_set) ①
{1, 3, 195, 6, 4, 8, 76, 15, 17, 18, 51, 30, 127}
>>> b_set.symmetric_difference(a_set) ②
{1, 195, 4, 3, 6, 8, 76, 15, 17, 18, 51, 30, 127}
>>> b_set.union(a_set) == a_set.union(b_set) ③
True
>>> b_set.intersection(a_set) == a_set.intersection(b_set) ④
True
>>> b_set.difference(a_set) == a_set.difference(b_set) ⑤
False
最后,有几个您可能会问到的问题。
>>> a_set = {1, 2, 3}
>>> b_set = {1, 2, 3, 4}
>>> a_set.issubset(b_set) ①
True
>>> b_set.issuperset(a_set) ②
True
>>> a_set.add(5) ③
>>> a_set.issubset(b_set)
False
>>> b_set.issuperset(a_set)
False
可在 if 这样的 布尔类型上下文环境中 使用集合。
>>> def is_it_true(anything):
... if anything:
... print("yes, it's true")
... else:
... print("no, it's false")
...
>>> is_it_true(set()) ①
no, it's false
>>> is_it_true({'a'}) ②
yes, it's true
>>> is_it_true({False}) ③
yes, it's true
创建字典非常简单。其语法与 集合 的类似,但应当指定键值对而不是值。有了字典后,可以通过键来查找值。
>>> a_dict = {'server': 'db.diveintopython3.org', 'database': 'mysql'} ①
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict['server'] ②
'db.diveintopython3.org'
>>> a_dict['database'] ③
'mysql'
>>> a_dict['db.diveintopython3.org'] ④
Traceback (most recent call last):
File "" , line 1, in <module>
KeyError: 'db.diveintopython3.org'
字典没有预定义的大小限制。可以随时向字典中添加新的键值对,或者修改现有键所关联的值。继续前面的例子:
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'mysql'}
>>> a_dict['database'] = 'blog' ①
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'blog'}
>>> a_dict['user'] = 'mark' ②
>>> a_dict ③
{'server': 'db.diveintopython3.org', 'database': 'blog', 'user': 'mark'}
>>> a_dict['user'] = 'dora' ④
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'blog', 'user': 'dora'}
>>> a_dict['User'] = 'mark' ⑤
>>> a_dict
{'server': 'db.diveintopython3.org', 'database': 'blog', 'user': 'dora', 'User': 'mark'}
字典并非只能用于字符串。字典的值可以是任何数据类型,包括整数、布尔值、任何对象,甚至是其它的字典。而且就算在同一字典中,所有的值也无须是同一类型,您可根据需要混合匹配。字典的键要严格得多,可以是字符串、整数和其它一些类型。在同一字典中也可混合、匹配使用不同数据类型的键。
实际上,您已经在 your first Python program 见过一个将非字符串用作键的字典了。
SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
让我们在交互式 shell 中剖析一下:
>>> SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], 1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
>>> len(SUFFIXES) ①
2
>>> 1000 in SUFFIXES ②
True
>>> SUFFIXES[1000] ③
['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
>>> SUFFIXES[1024] ④
['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
>>> SUFFIXES[1000][3] ⑤
'TB'
空字典为假值;所有其它字典为真值。
可以在 if 这样的 布尔类型上下文环境中 使用字典。
>>> def is_it_true(anything):
... if anything:
... print("yes it's true")
... else:
... print("no it's false")
...
>>> is_it_true({}) ①
no it's false
>>> is_it_true({'a': 1}) ②
yes it's true
None 是 Python 的一个特殊常量。它是一个 空 值。None 与 False 不同。None 不是 0 。None 不是空字符串。将 None 与任何非 None 的东西进行比较将总是返回 False
None 是唯一的空值。它有着自己的数据类型(NoneType)。可将 None 赋值给任何变量,但不能创建其它 NoneType 对象。所有值为 None 变量是相等的。
>>> type(None)
<class 'NoneType'>
>>> None == False
False
>>> None == 0
False
>>> None == ''
False
>>> None == None
True
>>> x = None
>>> x == None
True
>>> y = None
>>> x == y
True
在 布尔类型上下文环境中, None 为假值,而 not None 为真值。
>>> def is_it_true(anything):
... if anything:
... print('ok')
... else:
... print('no')
...
>>> is_it_true(None)
no
>>> is_it_true(not None)
ok
布尔运算
数值类型
序列类型
集合类型
映射类型
fractions【分数】 模块
math【数学】 模块
PEP 237:统一长整数和整数
PEP 238:修改除法运算符