Python 有多种内置数据类型,以下是比较重要的一些:

  1. Booleans(布尔型) 或为 True(真) 或为 False(假)。
  2. Numbers(数值型) 可以是 Integers(整数)(1 和 2)、Floats(浮点数)(1.1 和 1.2)、Fractions(分数)(1/2 和 2/3);甚至是 Complex Number(复数)。
  3. Strings(字符串型) 是 Unicode 字符序列,例如: 一份 HTML 文档。
  4. Bytes(字节) 和 Byte Arrays(字节数组), 例如: 一份 JPEG 图像文件。
  5. Lists(列表) 是值的有序序列。
  6. Tuples(元组) 是有序而不可变的值序列。
  7. Sets(集合) 是装满无序值的包裹。
  8. Dictionaries(字典) 是键值对的无序包裹。

整数

1
2
3
4
5
6
7
8
9
10
11
>>> 2+2
4
>>> # 这是注释
... 2+2
4
>>> 2+2 # 代码同一行的注释
4
>>> (50-5*6)/4
5.0
>>> 8/5 # 整数相除时并不会丢失小数部分
1.6

Python3没有"long int"类型,事实上Python的整数可以任意大(注: 只是理论上的,再咋地存储器也不是无限的)

整数和进制:二进制以 0b 引导,八进制以 0o 引导,十六进制则以 0x 引导,大写字母也可以使用

浮点数

浮点数的例子如 \(3.23\) , \(52.3E-4\) 。E代表10的幂,在这里 \(52.3E-4\) 等于 \(52.3\times{}10^{-4}\) 。

在混合计算时,Pyhton 会把整型转换成为浮点数:

1
2
3
4
>>> 3 * 3.75 / 1.5
7.5
>>> 7.0 / 2
3.5

浮点数使用的是双精度。如果希望得到更精确的结果,可以使用 decimal 模块。例如:

1
2
3
4
5
>>> import decimal
>>> a = decimal.Decimal(9876) # 可以接受整数或字符串作为参数
>>> b = decimal.Decimal("54321.012345678987654321") # 小数必须用字符串!
>>> a + b
Decimal('64197.012345678987654321')

有些情况下,浮点数与 decimal.Decimals 在精度上的差别会变得很明显:

1
2
3
4
5
6
7
8
>>>23 / 1.05
21.90476194761905
>>>print(23 / 1.05)
21.9047619048
>>>print(decimal.Decimal(23) / decimal.Decimal("1.05"))
21.90476190476190476190476190
>>>decimal.Decimal(23) / decimal.Decimal("1.05")
Decimal('21.90476190476190476190476190')

分数

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> import fractions
>>> x = fractions.Fraction(1, 3)
>>> x
Fraction(1, 3)
>>> x * 2
Fraction(2, 3)
>>> y = fractions.Fraction(6, 4)
>>> y
Fraction(3, 2)
>>> x == 1 / (2 * y)
True
>>> fractions.Fraction(0, 0)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "/usr/lib/python3.3/fractions.py", line 167, in __new_
_
raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
ZeroDivisionError: Fraction(0, 0)
>>>

复数

复数也有支持。虚数部分写得时候要加上后缀:j 或 i。实部非零的复数被写作 (real+imagj),也可以通过函数 complex(real, imag) 生成。

1
2
3
4
5
6
7
8
9
10
>>> 1j * 1J
(-1+0j)
>>> 1j * complex(0, 1)
(-1+0j)
>>> 3+1j*3
(3+3j)
>>> (3+1j)*3
(9+3j)
>>> (1+2j)/(1+1j)
(1.5+0.5j)

复数总是表达为两个浮点数, 实部和虚部. 要从复数 z 中抽取这些部分,使用 z.realz.imag

1
2
3
4
5
>>> a=1.5+0.5j
>>> a.real
1.5
>>> a.imag
0.5

字符串

单引号

你可以将字符串放到单引号中,所有空白字符即空格和tab都回原封保留。

双引号

双引号和单引号的效果完全相同。

三引号

在指定多行字符串的时候可以利用三引号("""'''), 在三引号中还能自由的使用单引号和双引号. 一个例子:

 '''This is a multi
 This is the second
 "What's your name?
 He said "Bond, Jam
 '''

转义字符

假设你想要一个带单引号(’)的字符串,你会怎么写呢?例如 What's your name?, 你不能写成 'What's your name?',因为Python会搞不清字符串是从哪开始又是从哪结束。所以你需要告诉Python字符串中间的单引号并不是字符串的结束标志。

利用转义字符可以做到这点。将单引号替换成 \' —— 注意反斜杠,这样字符串就变成 What's your name? 了。另一个办法是使用双引号 "What's your name?" 。不过当你需要双引号的时候和单引号的情况类似,必须加上反斜杠 \",而反斜杠也一样必须表示成 \\

如果你需要一个双行字符串呢? 一个办法是使用前面提到的三引号,或者使用换行的转义字符\n开始新的一行。

还有一个常用的转义字符是\t,用以输出制表符。Python中的转义字符很多,我只列举了最常用的。

另一个值得注意的地方是在一个字符串末尾的反斜杠表示这个字符串将被合并到下一行,例如:

1
2
"This is the first sentence.\
This is the second sentence."

上面的字符串等价于"This is the first sentence. This is the second sentence.".

原始字符串

如果你想要指示某些不需要如转义符那样的特别处理的字符串,那么你需要指定一个原始字符串。原始字符串通过给字符串加上前缀r或R来指定。例如r"Newlines are indicated by \n".

字符串是不可变类型

虽然这看起来像是一件坏事,但实际上它不是。我们将会在后面的程序中看到为什么我们说它不是一个缺点。

字面字符串连接

如果你将两个字面字符串相邻,它们会被Python自动合并到一起。

例如'What\'s ' 'your name?'会变为"What's your name?"

  • C++程序员请注意:Python没有单独的字符类型char,不过也没这个必要。我保证你不会为此烦恼。
  • Perl/PHP程序员请注意:单引号和双引号字符串完全相同,没有任何区别。
  • 正则表达式用户请注意 :永远使用原始字符串编写正则表达式,否则会需要大量的反斜杠,例如反向引用可以表示为 '\\1'r'\1'

字符串的format方法

有时我们需要使用额外信息构造字符串,这时format就很有用了(有点像C语言的printf函数)。

1
2
3
4
5
6
#!/usr/bin/Python
# Filename: str_format.py
age = 25
name = 'Swaroop'
print('{0} is {1} years old'.format(name, age))
print('Why is {0} playing with that Python?'.format(name))

输出:

1
2
3
$ Python str_format.py
Swaroop is 25 years old
Why is Swaroop playing with that Python?

format 如何工作

一个string可以含有某些格式说明符,随后调用的format将用你提供给它的参数替换对应的格式说明符。

观察上面的例子, {0}对应变量name,它也是format的第1个参数。与之类似{1}对应format的第2个参数age。注意Python是以0开始开始计数的。

要注意我们也可以使用字符串连接达到同样的目的: name + ' is ' + str(age) + ' years old'。但这种方式看起来太乱,容易出错。而且需要手动将变量转换为字符串,而format可以为我们代劳。最后, 使用format我们可以改变消息的形式,而不用修改传给format的变量,反之一样。

从Python 3.1开始,忽略字段名成为可能。即下面的用法是允许的:

1
2
>>> "{}{}{}".format("python","can","count")
'python can count'

format的本质就是将其参数替换到字符串中的格式说明符, 下面是一些更复杂的使用方式:

1
2
3
4
5
6
7
8
9
10
11
>>> '{0:.3}'.format(1/3) # 格式化规约,小数点后保留3位
'0.333'
>>> '{0:_^11}'.format('hello') # 以下划线填充,中间对齐,宽度为11位长
'___hello___'
>>> '{name} wrote {book}'.format(name='Swaroop', book='A Byte of Python') # 字段名格式化
'Swaroop wrote A Byte of Python'
>>> stock = ['paper', 'envelopes', 'notepads', 'pens', 'paper clips']
>>> "We have {0[1]} and {0[2]} in stock".format(stock)
'We have envelopes and notepads in stock'
>>> "math.pi == {0.pi} sys.maxunicode == {1.maxunicode}".format(math, sys)
'math.pi == 3.141592653589793 sys.maxunicode == 1114111'

关于格式说明符的具体信息见Python增强提议No.3101

format 格式说明语言

可以通过format 格式说明语言(Format Specification Mini-Language)很容易的实现对整数、浮点数以及字符串的格式的更精确的控制。

一个通用的标准格式说明语言如下:

1
2
3
4
5
6
7
format_spec ::= [[fill]align][sign][#][0][width][,][.precision][type]
fill ::=
align ::= "<" |="" "="">" | "=" | "^"
sign ::= "+" | "-" | " "
width ::= integer
precision ::= integer
type ::= "b" | "c" | "d" | "o" | "s" | "x" | "X" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "%"

align 对齐选项

选项 作用
< 强制在可用空间中左对齐(该选项对于大部分对象是默认选项)
> 强制在可用空间中右对齐(该选项对于数值型对象是默认选项)
= 强制在可用空间中在正负号(如果有的话)之后,有效数值之前进行填充。该选项常用于打印‘+000000120’这类数值。该对齐选项只对数值型对象可用。
^ 强制在可用空间中居中对齐。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> s = "The sword of truth"
>>> "{0}".format(s) # default formatting
"The sword of truth"
>>> "{0:25}".format(s) # minimum width 25
'The sword of truth '
>>> "{0:>25}".format(s) # right align, minimum width 25
' The sword of truth'
>>> "{0:^25}".format(s) # center align, minimum width 25
' The sword of truth '
>>> "{0:-^25}".format(s) # - fill, center align, minimum width 25
'---The sword of truth----'
>>> "{0:.<25}".format(s) # . fill, left align, minimum width 25
'The sword of truth.......'
>>> "{0:.10}".format(s) # maximum width 10
'The sword '

要注意的是,如果指定了一个填充字符(fill),就必须同时指定对齐字符(align)。

sign 符号选项

“sign”说明字符只可用于数值型参数,它用于确定是否打印正负号。

选项 作用
+ 不管该数值为正数还是负数,都要打印正负号。
- 只当该数值为负数时,才打印负号。(默认)
SPC 为正数输出空格,为负数输出负号。

‘#’ 选项

"#"选项用于告诉解释器同时使用“备用形式”(Alternate Form)进行转换,以获取某种基数进制为前缀的输出。后面可以带boxXdu几种类型说明符。参见 type选项 。

示例:

1
2
3
4
>>> "{0:b} {0:o} {0:x} {0:X}".format(14613198)
'110111101111101011001110 67575316 deface DEFACE'
>>> "{0:#b} {0:#o} {0:#x} {0:#X}".format(14613198)
'0b110111101111101011001110 0o67575316 0xdeface 0XDEFACE'

‘0’ 选项

在对齐时使用0进行填充。我们可以以两种不同的方式用0进行填充:

1
2
3
4
5
6
7
8
>>> "{0:0=12}".format(8749203) # 0 fill, minimum width 12
'000008749203'
>>> "{0:0=12}".format(-8749203) # 0 fill, minimum width 12
'-00008749203'
>>> "{0:012}".format(8749203) # 0-pad and minimum width 12
'000008749203'
>>> "{0:012}".format(-8749203) # 0-pad and minimum width 12
'-00008749203'

前两个实例使用0字符作为填充字符(fill),填充位置在符号与数字本身之间(=);后两个实例要求最小宽度为12,并使用0填充空白(pad)。

‘,’ 选项

使用“,”选项将告诉解释器使用逗号进行分组。例如:

1
2
>>> "{0:,}{0:*>13,}".format(int(2.39432185e6))
'2,394,321****2,394,321'

type 选项

最后,“type”选项决定了数据的呈现形式。针对不同数值类型有不同的类型选项:

用于字符串的有效类型有:

类型 用途
s 字符串格式。字符串类型默认都会带上这个选项,因此允许忽略。
s等效

用于整型的有效类型有:

类型 用途
b 二进制形式。
d 十进制形式。
o 八进制形式。
x 小写十六进制形式。
X 大写十六进制形式。
c 输出整数对应的Unicode字符。
n 以场所敏感的方式输出数字。其作用与d相同。
d等效

用于浮点数的有效类型有:

类型 用途
e 使用小写字母e的指数形式
E 使用大写字母E的指数形式
f 标准的浮点形式
g “通常”格式,与f的作用等同,除非数字特别大(在这种情况下与e作用等同)
G g几乎等同,但总是使用fE
n 以场所敏感的方式输出数字,其作用与g相同
% 使用百分比的形式输出数字。产生的数字结果使用f并附加一个%字符的格式输出
g相似,但小数点后至少保留一位数字,且默认精度为12。

字节

一个不可变(immutable)的 Unicode 编码的字符序列叫做 string。一串由 0 到 255 之间的数字组成的序列叫做 bytes 对象。

bytes 对象的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
>>> by = b'abcd\x65'
>>> by
b'abcde'
>>> type(by)
<class 'bytes'>
>>> len(by)
5
>>> by += b'\xff'
>>> by
b'abcde\xff'
>>> len(by)
6
>>> by[0]
97
>>> by[0] = 102 # 错误:字节和字符串一样都是不可变类型
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'bytes' object does not support item
assignment

bytes对象是不可变的:我们不可以给单个字节赋上新值。如果需要改变某个字节,可以组合使用字符串的切片和连接操作(效果跟字符串是一样的),或者我们也可以将bytes对象转换为bytearray对象。

bytearray对象

1
2
3
4
5
6
7
8
9
>>> by = b'abcd\x65'
>>> barr = bytearray(by)
>>> barr
bytearray(b'abcde')
>>> len(barr)
5
>>> barr[0] = 102
>>> barr
bytearray(b'fbcde')

不要混用bytes 和 strings

1
2
3
4
5
6
7
8
9
10
11
12
13
>>> by = b'd'
>>> s = 'abcde'
>>> by + s
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't concat bytes to str
>>> s.count(by)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't convert 'bytes' object to str
implicitly
>>> s.count(by.decode('ascii'))
1

字符串 VS. 字节

  • bytes -> strings: bytes 对象有一个 decode()方法,它使用某种字符编码作为参数,然后依照这种编码方式将 bytes 对象转换为字符串。
  • strings -> bytes: 对应地,字符串有一个 encode()方法,它也使用某种字符编码作为参数,然后依照它将串转换为 bytes 对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> a_string = '深入 Python'
>>> len(a_string)
9
>>> by = a_string.encode('utf‐8')
>>> by
b'\xe6\xb7\xb1\xe5\x85\xa5 Python'
>>> len(by)
13
>>> by = a_string.encode('gb18030')
>>> by
b'\xc9\xee\xc8\xeb Python'
>>> len(by)
11
>>> by = a_string.encode('big5')
>>> by
b'\xb2`\xa4J Python'
>>> len(by)
11
>>> roundtrip = by.decode('big5')
>>> roundtrip
'深入 Python'
>>> a_string == roundtrip
True

转换数据类型

如果需要将一个数据项从某种类型转换为另一种类型,那么可以使用语法 datatype (item) ,例如:

1
2
3
4
5
6
7
8
>>> int("45")
45
>>> type(45)
<class 'int'>
>>> str("92")
'92'
>>> type('92')
<class 'str'>

int()转换可以允许头尾处带有空格,因此,int(' 45 ') 也是正确的。str()转换几乎可以应用于所有数据项。

None

None 是 Python 的一个特殊常量。它是一个 空 值。None 与 False 不同。None 不是 0 。None 不是空字符串。将 None 与任何非 None 的东西进行比较将总是返回 False 。

None 是唯一的空值。它有着自己的数据类型(NoneType)。可将 None 赋值给任何变量,但不能创建其它 NoneType 对象。所有值为 None 变量是相等的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> 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

Python 和其他编程语言数据类型的比较

  • 静态类型语言

一种在编译期间就确定数据类型的语言。大多数静态类型语言是通过要求在使用任一变量之前声明其数据类型来保证这一点的。Java 和 C 是静态类型语言。

  • 动态类型语言

一种在运行期间才去确定数据类型的语言,与静态类型相反。vs.ript 和 Python 是动态类型的,因为它们确定一个变量的类型是在您第一次给它赋值的时候。

  • 强类型语言

一种总是强制类型定义的语言。Java 和 Python 是强制类型定义的。您有一个整数,如果不明确地进行转换 ,不能将把它当成一个字符串。

  • 弱类型语言

一种类型可以被忽略的语言,与强类型相反。vs.ript 是弱类型的。在 vs.ript 中,您可以将字符串 ‘12’ 和整数 3 进行连接得到字符串’123’,然后可以把它看成整数 123 ,所有这些都不需要任何的显示转换。

所以说 Python 既是动态类型语言 (因为它不使用显示数据类型声明),又是强类型语言 (因为只要一个变量获得了一个数据类型,它实际上就一直是这个类型了)。

列表

列表是一种用于保存有序元素集合的数据结构,即你可以在列表中存储元素序列。

考虑一个购物清单,上面有你需要购买的物品列表,只不过你可能希望以行分隔它们而到了python变成了逗号。这样想来就容易理解列表了吧。

列表元素应该被封闭在方括号中,列表的元素用逗号分隔,这样Python才会明白你指定的是一个列表(不加方括号会被认为是元组)。

一但列表创建完毕,你可以对其元素进行添加,删除和搜索。

正因为可以执行添加和删除操作,我们将列表称作可变类型,即这种类型可以被修改。

如果你想知道list对象的所有方法,详见help(list)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/python
# Filename: using_list.py
# This is my shopping list
shoplist = ['apple', 'mango', 'carrot', 'banana']
print('I have', len(shoplist), 'items to purchase.')
print('These items are:', end=' ')
for item in shoplist:
print(item, end=' ')
print('\nI also have to buy rice.')
shoplist.append('rice')
print('My shopping list is now', shoplist)
print('I will sort my list now')
shoplist.sort()
print('Sorted shopping list is', shoplist)
print('The first item I will buy is', shoplist[0])
olditem = shoplist[0]
del shoplist[0]
print('I bought the', olditem)
print('My shopping list is now', shoplist)

输出

1
2
3
4
5
6
7
8
9
10
$ python using_list.py
I have 4 items to purchase.
These items are: apple mango carrot banana
I also have to buy rice.
My shopping list is now ['apple', 'mango', 'carrot', 'banana', 'rice']
I will sort my list now
Sorted shopping list is ['apple', 'banana', 'carrot', 'mango', 'rice']
The first item I will buy is apple
I bought the apple
My shopping list is now ['banana', 'carrot', 'mango', 'rice']

说明

变量shoplist是某个购物人的购物清单。我们只在shoplist中存储被购买物品的名字的字符串,但你也可以为列表增加任何其它种类的对象,包括数字甚至是其它列表。

我们通过for…in迭代列表元素,现在你一定意识到一个列表也是一个序列了吧。有关序列的特点我们会在后节讨论。

注意print的end关键字实参,它指定我们希望以空格结束输出而不是通常的换行

接下来我们使用列表对象的append方法为列表添加一个新的元素。为了确定元素真的被添加进去了,我们简单的将列表传给print函数,print函数整洁的将列表内容打印出来。

随后我们使用列表的sort方法对列表进行排序,紧记sort会影响列表本身而不是返回一个被修改后的列表。这与字符串的工作方式不同。这也是为什么说类标是可变类型而字符串是不可变类型的原因。

然后当在市场购买一样东西后,我们希望将其从列表中删除,del语句正是用武之地。在这里我们指出希望删除列表中的哪个元素,del就将这个元素从列表中删除。我们指定的是希望删除列表的第一个元素,因此我们使用`del shoplist[0]``(回想一下,python的索引从0开始)。

元组

元组用于保存各种各样的对象。它与列表很相似,但它缺少列表提供的大量功能。

列表的一个主要特点就象字符串一样,它是不可变类型,也就是说你不可以修改元组

元组通过一组以逗号分隔的元素定义,并以一个可选的小括号闭合。

元组通常用于这样的情形,一个语句或一个用户定义的函数能够安全的假设其使用的一组值(即元组值)不会发生改变

如果你想知道tuple对象的所有方法,详见help(tuple)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/python
# Filename: using_tuple.py
zoo = ('python', 'elephant', 'penguin') # 注意小括号是可选的
print('Number of animals in the zoo is', len(zoo))
new_zoo = ('monkey', 'camel', zoo)
print('Number of cages in the new zoo is', len(new_zoo))
print('All animals in new zoo are', new_zoo)
print('Animals brought from old zoo are', new_zoo[2])
print('Last animal brought from old zoo is', new_zoo[2][2])
print('Number of animals in the new zoo is',
len(new_zoo)-1+len(new_zoo[2]))

输出

1
2
3
4
5
6
7
$ python using_tuple.py
Number of animals in the zoo is 3
Number of cages in the new zoo is 3
All animals in new zoo are ('monkey', 'camel', ('python', 'elephant', 'penguin'))
Animals brought from old zoo are ('python', 'elephant', 'penguin')
Last animal brought from old zoo is penguin
Number of animals in the new zoo is 5

说明

变量zoo引用一个元组。我们看到len函数可以得到元组的长度。这也表明元组同样是一个序列类型

因为老动物园歇菜了,于是我们将这些动物转移到一个新的动物园。因此元组new_zoo既包含已有的动物又包含从老动物园转移过来的新动物。

言归正传,注意一个包含在其它元组内的元组并不会丢失它的身份

像列表一样,我们可以通过一对方括号指定元素的位置访问这个元素。这叫做索引操作符。

小括号

虽然小括号是可选的,但我强烈建议你坚持使用小括号,这样一眼就能看出它是个元组,尤其还能避免出现歧义。

例如,print(1, 2, 3)print((1, 2, 3))是不同的 – 前者打印3个数字而后者打印一个元组(包含3个数字)。

拥有0个或1个元素的元组

一个空元组通过空小括号创建,例如myempty = ()

不过,指定一个单元素元组就不那么直观了。你必须在其仅有的一个元素后跟随一个逗号,这样python才能区分出。你要的是元组而不是一个被小括号包围的对象的表达式。例如你想要一个包含值为2的单元素元组,则必须写成singleton = (2, )

利用元组同时赋多个值

示例1

1
2
3
4
5
6
7
8
>>> v = ('a', 2, True)
>>> (x, y, z) = v
>>> x
'a'
>>> y
2
>>> z
True

该特性有多个用途,假设需要将某个名称指定某个特定范围的值。可以使用内建的 range() 函数进行多变量赋值以快速地进行连续变量赋值。

示例2

1
2
3
4
5
6
7
8
>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
SATURDAY, SUNDAY) = range(7)
>>> MONDAY
0
>>> TUESDAY
1
>>> SUNDAY
6

内建的 range() 函数构造了一个整数序列。(从技术上来说,range() 函数返回的既不是列表也不是元组,而是一个 迭代器 )

这种语法也暗示出在python中快速交换两个变量值的方法:

1
2
3
4
>>> a = 5; b = 8
>>> a, b = b, a
>>> a, b
(8, 5)

利用元组返回多个值

你是否希望过从函数返回两个不同的值?做到这点使用元组即可。

示例

1
2
3
4
5
6
7
8
>>> def get_error_details():
... return (2, 'second error details')
...
>>> errnum, errstr = get_error_details()
>>> errnum
2
>>> errstr
'second error details'

注意a, b = <某些表达式>的使用,它会将表达式的结果解释为带有两个值的元组。

如果你希望将结果解释成(a, <其它值>)的形式,那么你要做的就象在函数形参中的那样:

1
2
3
4
5
>>> a, *b = [1, 2, 3, 4]
>>> a
1
>>> b
[2, 3, 4]

字典

典型的key-value结构。字典就像通讯录,只要知道联系人的名字就能找到他的地址或详细信息。即我们将键(名字)与值(相关信息)联系到一起。

字典键的要求

  1. 字典键必须是唯一的;
  2. 字典键为不可变对象;
  3. 字典键是区分大小写的。

基本上这意味着只能将简单的对象作为键。相比之下,字典的值可以是可变或不可变对象,并且可以是任何数据类型。

声明格式

1
d = {key1:value1, key2:value2}

也可以利用dict类的构造函数实例化一个字典对象:

1
d = dict(key1=value1, key2=value2)

其中键和值由分号分隔而所有的键值对用逗号分隔,并且它们被括在一对大括号内

记住字典中的键值对是无序的,你只能通过 key 来获取对应的 value。 你可以执行help(dict)找到字典所有方法的列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/usr/bin/python
# Filename: using_dict.py
# 'ab'是'a'ddress'b'ook的缩写
ab = { 'Swaroop' : 'swaroop@swaroopch.com',
'Larry' : 'larry@wall.org',
'Matsumoto' : 'matz@ruby-lang.org',
'Spammer' : 'spammer@hotmail.com'
}
print("Swaroop's address is", ab['Swaroop'])
# 删除一个键值对
del ab['Spammer']
print('\nThere are {0} contacts in the address-book\n'.format(len(ab)))
for name, address in ab.items():
print('Contact {0} at {1}'.format(name, address))
# 修改一个键值对
ab['Larry'] = 'larry@larry.org'
# 添加一个键值对,和修改键值对完全一样
ab['Guido'] = 'guido@python.org'
# 查找某个键值对
if 'Guido' in ab: # 或者写成 ab.has_key('Guido')
print("\nGuido's address is", ab['Guido'])

输出

1
2
3
4
5
6
7
$ python using_dict.py
Swaroop's address is swaroop@swaroopch.com
There are 3 contacts in the address-book
Contact Swaroop at swaroop@swaroopch.com
Contact Matsumoto at matz@ruby-lang.org
Contact Larry at larry@wall.org
Guido's address is guido@python.org

说明

我们使用先前介绍的语法创建字典ab。然后使用在列表和元组部分讨论过的索引操作符指定字典键访问键值对。

我们的老朋友del语句可以帮助我们删除键值对。只需简单的为索引操作符指定被删除的键,再将其传给del语句就了。执行删除操作时我们无需理会键所对应的值。

接下来我们使用字典的items方法访问字典的键值对,它会返回一个包含键值对元组的列表 —— 值跟在键后面。在for…in循环中我们检索每个键值对并将它们分别赋给变量name和address,之后在循环体中打印它们。

利用索引操作符访问一个键并对其赋予一个值我们可以增加一个新的键值对,就象本例中的Guido那样。

通过dict类的has_key可以检查字典中是否存在某个键值对。

关键字实参和字典

如果你已经在函数中使用过关键字实参,那么你也已经使用过字典了!

你可以这样理解:你在函数定义时的形参列表中指定了键值对,当你在函数中访问这些变量的时候只不过是在访问一个字典。

(在编译器设计的术语中这被称作符号表)

序列

列表,元组和字符串都是序列的例子,但到底序列是啥呢?为什么它对我们的意义如此特别?

序列最主要的特点在于支持成员从属测试(即,表达式中的innot in操作)索引操作。其中索引操作允许我们直接地获取序列中的指定元素。 以上说到的三种序列类型 – lists,tuples,strings 还支持一种切片操作,允许我们得到序列的一个切片,即序列的部分

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/python
# Filename: seq.py
shoplist = ['apple', 'mango', 'carrot', 'banana']
name = 'swaroop'
# Indexing or 'Subscription' operation
print('Item 0 is', shoplist[0])
print('Item 1 is', shoplist[1])
print('Item 2 is', shoplist[2])
print('Item 3 is', shoplist[3])
print('Item -1 is', shoplist[-1])
print('Item -2 is', shoplist[-2])
print('Character 0 is', name[0])
# Slicing on a list
print('Item 1 to 3 is', shoplist[1:3])
print('Item 2 to end is', shoplist[2:])
print('Item 1 to -1 is', shoplist[1:-1])
print('Item start to end is', shoplist[:]) # 全切片
# Slicing on a string
print('characters 1 to 3 is', name[1:3])
print('characters 2 to end is', name[2:])
print('characters 1 to -1 is', name[1:-1])
print('characters start to end is', name[:])

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ python seq.py
Item 0 is apple
Item 1 is mango
Item 2 is carrot
Item 3 is banana
Item -1 is banana
Item -2 is carrot
Character 0 is s
Item 1 to 3 is ['mango', 'carrot']
Item 2 to end is ['carrot', 'banana']
Item 1 to -1 is ['mango', 'carrot']
Item start to end is ['apple', 'mango', 'carrot', 'banana']
characters 1 to 3 is wa
characters 2 to end is aroop
characters 1 to -1 is waroo
characters start to end is swaroop

说明

索引也可以是负数,这时候位置将从序列尾开始计算。所以,shoplist[-1]引用序列的最后一个元素,shoplist[-2]为倒数第二个。

[-1]来获取最后序列的一个元素,这点非常有用!

切片操作的使用方法是先指定序列名后跟一对方括号,其中包含一对可选的由分号分隔的数字。注意这与你至今使用的索引操作非常相似。记住数字是可选的,但分号不可以省略

切片操作中的第一个数字(分号前)指出切片的开始位置而第二个数字(分号后)指定将在哪个位置结束。如果省略第一个数字则python将以序列的起点为开始处,而省略第二个数字时切片会停止在序列的结尾处。

注意切片将在开始处开始,结束于结尾处之前,即包括开始处但不包括结尾处。(注:比如a[1:10],返回的是a[1]到a[9]不包括a[10])。

因此,shoplist[1:3]开始于索引1,包括索引2但止于索引3,即返回一个包含两个元素的切片。与之类似shoplist[:]将返回整个序列的拷贝,这是对列表进行复制的一条捷径。

你还能以负索引切片。负数代表从序列的末尾开始反向计算位置。例如shooplist[:-1]返回整个序列,但不包括未尾的元素。

另外你还可以为切片提供第三个实参,它代表步长(默认为1)。步长默认为1将返回文本的一个连续部分。而给定一个负步长-1将返回反转后的文本。

1
2
3
4
5
6
7
8
9
>>> shoplist = ['apple', 'mango', 'carrot', 'banana']
>>> shoplist[::1]
['apple', 'mango', 'carrot', 'banana']
>>> shoplist[::2]
['apple', 'carrot']
>>> shoplist[::3]
['apple', 'banana']
>>> shoplist[::-1]
['banana', 'carrot', 'mango', 'apple']

注意当步长为2时,我们得到索引为0,2…的元素,步长为3时得到0,3…,以此类推。

集合

集合是简单对象的无序集合,适合当更关心集合中的元素是否存在而不是它们的顺序或是它们出现的次数的时候。

使用集合,你可以测试从属关系,是否一个集合是另一个集合的子集,或是寻找两个集合的交集等等。

如果你想知道set对象的所有方法,详见help(set)

示例1

1
2
3
4
5
6
7
8
9
10
>>> a_set = {1}
>>> a_set
{1}
>>> type(a_set)
<class 'set'>
>>> a_set = {1, 2}
>>> a_set
{1, 2}
>>> {1, 2}
{1, 2}

示例2

也可以利用set类的构造函数实例化一个集合对象。这个例子示例以列表为基础创建集合。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> a_list = ['brazil', 'russia', 'india', 'russia']
>>> bri = set(a_list)
>>> bri
{'brazil', 'russia', 'india'}
>>> 'india' in bri
True
>>> 'usa' in bri
False
>>> bric = bri.copy()
>>> bric.add('china')
>>> bric.issuperset(bri)
True
>>> bri.remove('russia')
>>> bri & bric # OR bri.intersection(bric)
{'brazil', 'india'}

解析(Comprehension)

列表解析

列表解析用于从一个现有的列表派生出一个新的列表。

假设你有一个数字列表,你想让其中所有大于2的元素乘以2并组成一个新的列表。类似问题正是使用列表解析的理想场合。

示例

1
2
3
4
5
6
#!/usr/bin/python
# Filename: list_comprehension.py
listone = [2, 3, 4]
listtwo = [2*i for i in listone if i > 2]
print(listtwo)

输出

1
2
$ python list_comprehension.py
[6, 8]

说明

当某些条件满足时(if i > 2)我们执行某些操作(2 * i),由此产生一个新列表。注意原始列表并不会被改变。

使用列表解析的好处在于当我们使用循环遍历元素并将其存储到新列表时可以减少样板代码量。

你可以在列表解析中使用任何的 Python 表达式, 包括 os 模块中用于操作文件和目录的函数。

1
2
3
4
5
6
7
>>> import os, glob
>>> glob.glob('*.xml')
['feed‐broken.xml', 'feed‐ns0.xml', 'feed.xml']
>>> [os.path.realpath(f) for f in glob.glob('*.xml')]
['c:\\Users\\pilgrim\\diveintopython3\\examples\\feed‐broken.xml',
'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed‐ns0.xml',
'c:\\Users\\pilgrim\\diveintopython3\\examples\\feed.xml']

字典解析

字典解析和列表解析类似,只不过它生成字典而不是列表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
>>> import os, glob
>>> metadata = [(f, os.stat(f)) for f in glob.glob('*test*.py')]
>>> metadata[0]
('alphameticstest.py', nt.stat_result(st_mode=33206,
st_ino=0, st_dev=0, st_nlink=0, st_uid=0, st_gid=0, st_size=2509,
st_atime=1247520344, st_mtime=1247520344, st_ctime=1247520344))
>>> metadata_dict = {f:os.stat(f) for f in glob.glob('*test*.py')} # 字典解析
>>> type(metadata_dict)
<class 'dict'>
>>> list(metadata_dict.keys())
['romantest8.py', 'pluraltest1.py', 'pluraltest2.py',
'pluraltest5.py',
'pluraltest6.py', 'romantest7.py', 'romantest10.py',
'romantest4.py',
'romantest9.py', 'pluraltest3.py', 'romantest1.py',
'romantest2.py',
'romantest3.py', 'romantest5.py', 'romantest6.py',
'alphameticstest.py',
'pluraltest4.py']
>>> metadata_dict['alphameticstest.py'].st_size
2509

其他同字典解析有关的小技巧

这里是一个可能有用的通过字典解析实现的小技巧:交换字典的键和值。

1
2
3
>>> a_dict = {'a': 1, 'b': 2, 'c': 3}
>>> {value:key for key, value in a_dict.items()}
{1: 'a', 2: 'b', 3: 'c'}

集合解析

同样,集合也有自己的集合解析的语法。它和字典解析的非常相似,唯一的不同是集合只有值而没有键:值对。

1
2
3
4
5
6
7
8
9
>>> a_set = set(range(10))
>>> a_set
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
>>> {x ** 2 for x in a_set}
{0, 1, 4, 81, 64, 9, 16, 49, 25, 36}
>>> {x for x in a_set if x % 2 == 0}
{0, 8, 2, 4, 6}
>>> {2**x for x in range(10)}
{32, 1, 2, 4, 8, 64, 128, 256, 16, 512}

解包(Unpack)

如果想把一个对象(例如列表、元组或迭代器)里包含的元素作为参数传给其他函数,需要把这个对象里的元素取出来,称为“解包”。例如,range()函数需要 start 和 stop 参数,如果想把一个包含两个元素的列表作为参数传给range()函数,那么可以在该对象前加上 * 操作符以把其包含的参数出来:

1
2
3
4
5
6
7
8
9
>>> list(range(3, 6)) # 使用分离的参数正常调用
[3, 4, 5]
>>> list(range([3, 6])) # 直接使用一个列表作为range()函数的参数会出错
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'list' object cannot be interpreted as an integer
>>> args = [3, 6]
>>> list(range(*args)) # 通过解包列表参数调用
[3, 4, 5]

同样的, 字典可以通过 ** 操作符来释放参数:

1
2
3
4
5
6
7
8
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !

将变量作为参数传给format

当前还在作用范围内的局部变量可以通过内置的 locals() 函数访问,该函数会返回一个字典,字典的键是局部变量名,字典的值则是对变量值的引用。现在,我们可以使用解包,将该字典里的元素作为参数提供给 str.format() 方法,解包操作符为 ** ,可应用于映射(比如字典)来产生一个适合于传递给函数的键-值列表,比如:

1
2
3
4
>>> element = "Silver"
>>> number = 47
>>> "Element {number} is {element}".format(**locals())
'Element 47 is Silver'

关于字符串的更多知识

字符串同样是一种对象并拥有很多方法!从检查字符串的一部分到删除其中的空格应有尽有!

你在程序中使用的所有字符串都是str类的对象。下面的例子会演示str类中的一些有用的方法。全部方法的列表,参见help(str)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python
# Filename: str_methods.py
name = 'Swaroop' # 这是一个字符串对象
if name.startswith('Swa'):
print('Yes, the string starts with "Swa"')
if 'a' in name:
print('Yes, it contains the string "a"')
if name.find('war') != -1:
print('Yes, it contains the string "war"')
delimiter = '_*_'
mylist = ['Brazil', 'Russia', 'India', 'China']
print(delimiter.join(mylist))

输出

1
2
3
4
5
$ python str_methods.py
Yes, the string starts with "Swa"
Yes, it contains the string "a"
Yes, it contains the string "war"
Brazil_*_Russia_*_India_*_China

说明

startswith方法用于确定字符串是否以指定的字符串开头。而in操作检查一个字符串是否是另一个字符串的一部分。

find方法用来寻找给定的字符串在字符串中的位置,如果没找到对应的子串则返回-1。

str类还有一个简洁的连接序列中每个字符串并返回连接后的字符串的方法join,其中每个字符串都将以指定的字符串分隔。