本节书摘来自异步社区《Python地理数据处理》一书中的第2章,第2.4节,作者: 【美】Chris Garrard(加勒德) 更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.4 数据类型
随着代码变得更加复杂,使用数字和字符串存储脚本需要的所有信息非常困难。幸运的是,你可以使用多种不同类型的数据结构,从简单的数字到复杂的对象,它们自身可以包含多种不同类型的数据。尽管这些对象类型的数量无限(因为你可以任意定义),但只有少量核心数据类型存在,其他复杂的数据结构都由此而生。这里简要讨论几个数据类型。细节方面,详见更为全面的Python文档,这里只做简要说明。
2.4.1 布尔型
一个布尔变量表示真值或假值。使用两个区分大小写的关键字——True和False,来表示这些值。它们可以用于标准的布尔运算,像这样:
>>> True or False
True
>>> not False
True
>>> True and False
False
>>> True and not False
True
当测试变量和执行布尔运算时,其他值也可以解析为真或假。例如,当使用布尔表达式时,0、None关键词、空白字符串、空列表、元组集和字典都解析为False,而其他所有都解析为True。在2.5节中,你会看到这样的例子。
2.4.2 数值型
可以使用Python来处理数字。然而,你可能没有想到还存在不同类型的数字。整型就是整数,如5、27或592;浮点型是带小数点的数字,如5.3、27.0或592.8。如果说27和27.0是不同的,你会感到惊讶吗?其中一方面是,它们可能会占用不同数量的内存,虽然具体(占用量)取决于你的操作系统和Python的版本。如果你正在使用Python 2.7版本,两个数字如何用于数学运算有很大的区别,因为整数会不考虑小数点后的数。下面来看Python 2.7这个例子:
>>> 27 / 7
3
>>> 27.0 / 7.0
3.857142857142857
>>> 27 / 7.0
3.857142857142857
正如你所看到的,如果用一个整数除以另一个整数,仍然以一个整数结束,即使结果仍有剩余的小数部分;如果在操作中使用一个或两个浮点的数字相除,则可以得到正确的答案。这种事情已经在Python 3.x中有所改变。现在无论哪种方式,你都可以得到浮点计算结果,但可以通过//符号截断除法操作来强制执行整除。
>>> 27 / 7
3.857142857142857
>>> 27 // 7
3
警告
Python 3.x针对整数默认执行浮点计算,但如果所有的输入都是整数,旧版本的Python将执行整数计算。这个整数计算通常会导致不理想的结果,例如2会代替2.4,在这种情况下,必须确保至少一个输入是浮点的。
幸运的是,有一个简单的方法来转换数值数据类型,但要注意转换浮点到整数是截取,而不是按指定的位数对数值进行四舍五入。
>>> float(27)
27.0
>>> int(27.9)
27
如果想对数值进行四舍五入,必须用round函数。
>>> round(27.9)
28
Python也支持复杂的数字,其中包含实部和虚部。你可能会记得,当对一个负数开平方根,这些值的结果。本书中不使用复杂的数字,如果感兴趣,你可以在python.org上阅读到更多内容。
2.4.3 字符串
字符串是文本值,例如'Hello world'。可以用单引号或双引号包括文本来创建一个字符串,这都无关紧要,但如果你在开头采用一种引号符号,结尾又采用另一种引号符号,Python就会无法识别。事实上,任意一种引号都可使得将引号作为字符串的一部分变得十分容易。例如,如果你的字符串里面需要单引号,在SQL语句中,就用双引号包括整个字符串,像这样:
sql = "SELECT * FROM cities WHERE country = 'Canada'"
如果你需要在字符串中包含相同类型的引号来描绘它,可以在引号前使用反斜杠。这里的第1个例子导致了一个错误,因为“Don’t”内的单引号结束了字符串,而这并不是你想要的。由于反斜杠的存在,第2个例子就成功了。
>>> 'Don't panic!'
File "<stdin>", line 1
'Don't panic!'
^
SyntaxError: invalid syntax
>>> 'Don\'t panic!'
"Don't panic!"
注意:在插入符(^)的地方,Python遇到了麻烦。这可以帮你缩小语法错误的范围。当双引号包括的字符串输出时,输出的结果不是字符串的一部分。在这种情况下,它明显是一个字符串,但如果字符串是“42”,就不一样了。如果用print函数,双引号就不会显示。
>>> print('Don\'t panic!')
Don't panic!
提示
虽然交互窗口中大多数示例不使用输出函数将结果输出到屏幕上,但你必须在脚本中用输出函数。如果你不采用,它就不会出现。在Python 3中,输出和其他函数一样,你必须将参数放入函数括号中。在Python 2中,输出是一个语句,并且不需要括号,但它们也不会打断任何东西。
1.连接字符串
你有多种方法来连接字符串。如果你只连接两个字符串,那么最简单、最快速的方法是使用运算符“+”:
>>> 'Beam me up ' + 'Scotty'
'Beam me up Scotty'
如果连接多个字符串,format方法是一个更好的选择。它可以将一些不全是字符串的值串起来,而且有些事是运算符“+”做不到的。要使用它,需要创建模板字符串,使用花括号作为占位符,然后传值去取代占位符的位置。你可以阅读Python在线文档,其中有很多你可以使用的复杂的格式化方法,但我们会介绍指定顺序的基本方法。这里,第1项传递给format来替代{ 0 }占位符,第2项取代{ 1 },以此类推:
>>> 'I wish I were as smart as {0} {1}'.format('Albert', 'Einstein')
'I wish I were as smart as Albert Einstein'
想查看数字占位符的不同作用,可以尝试交换一下它们的位置,并使其他内容保持不变:
>>> 'I wish I were as smart as {1}, {0}'.format('Albert', 'Einstein')
'I wish I were as smart as Einstein, Albert'
事实上,占位符引用特定值意味着,如果你需要在字符串中多次插入元素,可以在多个位置使用相同的占位符。这样,你就不必在传递给format的值列表中重复任何东西了。
2.转义字符
还记得先前你用于包含字符串里一个引号的反斜杠吗?这就是转义字符,还能够用于包含字符串里不能打印的字符。例如,“n”包含新的一行并且“t”代表缩进:
>>> print('Title:\tMoby Dick\nAuthor:\tHerman Melville')
Title: Moby Dick
Author: Herman Melville
事实上,Windows使用反斜杠作为路径分隔符为起步阶段的程序员使用Windows时造成焦虑,因为他们往往忘了单独的反斜杠不是反斜杠。例如,假设有一个cities.csv文件在d:temp文件夹下。尝试问问Python它是否存在:
>>> import os
>>> os.path.exists('d:\temp\cities.csv')
False
当知道该文件确实存在时,要知道为什么失败,可以尝试输出字符串:
>>> print('d:\temp\cities.csv')
d: emp\cities.csv
“t”被视为缩进符!有3种方法可以解决这个问题,即使用正斜杠或双反斜杠或前缀字符串r告诉Python忽略转义字符:
>>> os.path.exists('d:/temp/cities.csv')
True
>>> os.path.exists('d:\\temp\\cities.csv')
True
>>> os.path.exists(r'd:\temp\cities.csv')
True
如果复制和粘贴路径,我更喜欢最后一种方法,因为比起添加多个反斜杠,在开始处添加一个字符更容易。
2.4.4 列表和元组
列表是通过索引访问元素的有序集合。列表中的第1个元素索引为0,第2个元素索引为1,以此类推。元素不必是相同的数据类型。可以用一组方括号[]创建一个空列表,或者可以批处理直接填充。例如,下面创建了一个混合有数字和字符串的列表,然后访问其中的某些:
>>> data = [5, 'Bob', 'yellow', -43, 'cat']
>>> data[0]
5
>>> data[2]
'yellow'
还可以从列表的尾端开始偏移,最后一个元素的索引为-1:
>>> data[-1]
'cat'
>>> data[-3]
'yellow'
不局限于一次检索一个元素。你可以提供起点和终点的索引来提取切片或子列表。终点索引对应的元素不包括在返回值中。
>>> data[1:3]
['Bob', 'yellow']
>>> data[-4:-1]
['Bob', 'yellow', -43]
可以使用索引在列表中更改单个值,甚至切片:
>>> data[2] = 'red'
>>> data
[5, 'Bob', 'red', -43, 'cat']
>>> data[0:2] = [2, 'Mary']
>>> data
[2, 'Mary', 'red', -43, 'cat']
使用append可以添加一个元素到列表的末尾,用del来删除一个元素:
>>> data.append('dog')
>>> data
[2, 'Mary', 'red', -43, 'cat', 'dog']
>>> del data[1]
>>> data
[2, 'red', -43, 'cat', 'dog']
一个列表中有多少个元素或者它是否包含某个特定的值,都很容易发现:
>>> len(data)
5
>>> 2 in data
True
>>> 'Mary' in data
False
元组也是按顺序排列的元素集合,但他们一旦初始化,就无法更改。元组用圆括号包围,而不是方括号。你可以像操作列表一样,操作元组中的元素和测试实体。
>>> data = (5, 'Bob', 'yellow', -43, 'cat')
>>> data[1:3]
('Bob', 'yellow')
>>> len(data)
5
>>> 'Bob' in data
True
就像我说的,一旦元组初始化,就不允许改变:
>>> data[0] = 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
由于这个原因,当数据有可能会改变时,选用列表,而不是元组。
错误信息是朋友
当收到一个错误信息,一定要仔细看它提供的信息。因为这可以节省你解决问题的时间。最后一行是一个消息,给你一个这是什么问题的大致思路,如下:
>> data[0] = 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
可以从这个错误信息中推断出你的代码试图在编辑一个元组对象。在错误消息之前,你会看到一列代码在遇到问题之前被执行,这称为堆栈跟踪。在这个例子中,意味着交互窗口,所以行数没有帮助。下面是它通过两行代码的跟踪:
Traceback (most recent call last):
File "D:\Temp\trace_example.py", line 7, in <module>
y = add(x, '1') ←---第7行代码
File "D:\Temp\trace_example.py", line 2, in add
return n1 + n2 ←---第2行代码
TypeError: unsupported operand type(s) for +: 'int' and 'str'
最后一行告诉你,错误是由于试图让整数和字符串相加。通过代码跟踪发现问题开始于trace_example.py文件的第7行。第7行调用函数add,并且错误发生在该函数的第2行内。可以使用堆栈跟踪,以确定错误信息发生的地方,并且知道触发它的原始代码行。这个示例中,你知道可能是将错误的数据传递给第7行的函数,或是在第2行的add函数中存在一个错误。从这两个具体的地方去寻找错误。
2.4.5 集合
集合是无序元素的集合,但每一个值只能出现一次,这使得从列表中删除重复元素变得简单。例如,使用包含两个13的实例列表创建此集合,但只有一个13在结果集合中:
>>> data = set(['book', 6, 13, 13, 'movie'])
>>> data
{'movie', 6, 'book', 13}
可以添加新的值,如果他们已经存在于集合中,就会被忽略,例如这个例子中的“movie”:
>>> data.add('movie')
>>> data.add('game')
>>> data
{'movie', 'game', 6, 'book', 13}
集合不是有序的,所以不能访问特定的元素,然而,可以检查集合中是否存在某元素:
>>> 13 in data
True
使用集合也很容易做这些事情,如合并集合(union)或者查找两个集合中共同包含的元素(intersection):
>>> info = set(['6', 6, 'game', 42])
>>> data.union(info)
{6, 'movie', 13, 'game', 'book', '6', 42} ←---所有的值都来自于集合
>>> data.intersection(info) ←--只有两者的交集
{'game', 6}
你已经看到,可以使用集合从列表中删除重复项。一个确定列表中是否包含重复值的简单方法是从列表中创建集合,并检查集合和列表的长度是否相同。如果它们不一样,那么就知道在列表中有重复。
2.4.6 字典
字典是索引集合,除了索引不像列表中的偏移外,其他都类似于列表和元组。相反,可以选择称为“键”的索引值。键可以是数字、字符串或其他数据类型,因此可以引用它们的值。使用大括号创建一个新的字典:
>>> data = {'color': 'red', 'lucky number': 42, 1: 'one'}
>>> data
{1: 'one', 'lucky number': 42, 'color': 'red'}
>>> data[1]
'one'
>>> data['lucky number']
42
与列表一样,可以添加、更改和删除元素:
>>> data[5] = 'candy'
>>> data
{1: 'one', 'lucky number': 42, 5: 'candy', 'color': 'red'}
>>> data['color'] = 'green'
>>> data
{1: 'one', 'lucky number': 42, 5: 'candy', 'color': 'green'}
>>> del data[1]
>>> data
{'lucky number': 42, 5: 'candy', 'color': 'green'}
也可以测试某个键是否存在于字典中:
>>> 'color' in data
True
当不知道数据是什么时,这是一个存储数据的强大方式。例如,需要记住地理数据集中的每一个文件的空间范围,但是每当运行脚本时,数据集列表就发生改变。可以创建一个字典,用文件名作为键,其对应的空间范围作为值,然后这些信息以后可在你的脚本中使用。