目录 | 上一节 (1.7 函数) | 下一节 (2.2 容器)

2.1 数据类型和数据结构

本节以元组和字典为代表介绍数据结构。

原始数据类型

Python 有一些原始数据类型:

  • 整数
  • 浮点数
  • 字符串(文本)

空类型

email_address = None

None 常用作可选值或缺失值的占位符。它在条件语句中计算为 False

if email_address:
send_email(email_address, msg)

数据结构

实际的程序具有更复杂的数据。例如,关于股票的持有信息:

100 shares of GOOG at $490.10

这是一个包含三个部分的“对象”:

  • 股票的名称或符号("GOOG",字符串)
  • 股份数目(100,整数)
  • 价格(490.10,浮点数)

元组

元组是分组在一起的值的集合。

示例:

s = ('GOOG', 100, 490.1)

有时候会在语法上省略 ()

s = 'GOOG', 100, 490.1

特殊情况(0 元组,1 元组)。

t = ()            # An empty tuple
w = ('GOOG', ) # A 1-item tuple

元组一般用来表示简单的记录或结构。

通常,它是由多个部分组成的单个对象。这有一个很好的类比:元组就像数据库表中的一行。

元组的内容是有序的(类似于数组)。

s = ('GOOG', 100, 490.1)
name = s[0] # 'GOOG'
shares = s[1] # 100
price = s[2] # 490.1

但是,元组的内容无法修改。

>>> s[1] = 75
TypeError: object does not support item assignment

你可以基于当前元组创建一个新元组。

s = (s[0], 75, s[2])

元组打包

元组更多的是把相关的项打包到一个实体(entity)中。

s = ('GOOG', 100, 490.1)

然后,该元组很容易作为单个对象传递给程序的其它部分。

元组拆包

要在其它地方使用元组,可以把元组的各部分拆包为变量。

name, shares, price = s
print('Cost', shares * price)

左侧变量的数目必须与元组的结构匹配。

name, shares = s     # ERROR
Traceback (most recent call last):
...
ValueError: too many values to unpack

元组与列表

元组看起来像只读列表。但是,元组最常用于由多个部分组成的单项。列表通常是类型相同的项的集合,

record = ('GOOG', 100, 490.1)       # A tuple representing a record in a portfolio

symbols = [ 'GOOG', 'AAPL', 'IBM' ]  # A List representing three stock symbols

字典

字典是键到值的映射。有时,字典也称为哈希表(hash table)或关联数组(associative array)。键用作访问值的索引。

s = {
'name': 'GOOG',
'shares': 100,
'price': 490.1
}

常见操作

要从字典中获取值,请使用键名。

>>> print(s['name'], s['shares'])
GOOG 100
>>> s['price']
490.10
>>>

要添加或修改值,请使用键名进行分配。

>>> s['shares'] = 75
>>> s['date'] = '6/6/2007'
>>>

要删除值,请使用 del 语句。

>>> del s['date']
>>>

为什么使用字典?

当存在很多不同的值并且可能会修改或操作这些值时,字典很有用。字典使代码更具可读性。

s['price']
# vs
s[2]

练习

在上次的几个练习中,编写了一个取数据文件 Data/portfolio.csv 的程序 。使用 csv 模块,可以轻松地逐行读取文件。

>>> import csv
>>> f = open('Data/portfolio.csv')
>>> rows = csv.reader(f)
>>> next(rows)
['name', 'shares', 'price']
>>> row = next(rows)
>>> row
['AA', '100', '32.20']
>>>

尽管读取文件很容易,但是与读取数据相比,通常使用数据做更多的事情。例如,也许想存储它并对其执行一些计算。不幸的是,原始的数据“行”并不能这样做。例如,即使是简单的数学计算也不行。

>>> row = ['AA', '100', '32.20']
>>> cost = row[1] * row[2]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't multiply sequence by non-int of type 'str'
>>>

要执行更多的操作,通常需要以某种方式解释原始数据,并将其转换为更有用的对象类型,以便以后处理。有两种简单的方式可以选择:元组或者字典。

练习 2.1:元组

在交互式提示符下,创建以下代表上一行的元组,但数字列要转换为恰当的数字。

>>> t = (row[0], int(row[1]), float(row[2]))
>>> t
('AA', 100, 32.2)
>>>

使用这种方式,现在可以使用股份数目乘以价格来计算总价,

>>> cost = t[1] * t[2]
>>> cost
3220.0000000000005
>>>

在 Python 中,数学没用了吗?结果为什么是 3220.0000000000005?

这是计算机上浮点硬件的产物,只能在二进制(而不是十进制)中准确表示小数。即使是涉及十进制小数的简单计算,也会引入小的误差。这很正常,如果你之前没有见过,可能会有点惊讶。

虽然在所有使用浮点小数的编程语言中都会发生这种情况,但是打印的时候可以把它隐藏,例如:

>>> print(f'{cost:0.2f}')
3220.00
>>>

元组是只读的。可以通过尝试把股份数目改为 75 来验证这点。

>>> t[1] = 75
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>>

尽管无法更改元组的内容,但是始终可以创建一个全新的元组来替换旧的元组。

>>> t = (t[0], 75, t[2])
>>> t
('AA', 75, 32.2)
>>>

每当像这样重新分配现有变量名时,旧值就会被丢弃。虽然上面的赋值可能看起来像在修改元组,但实际上是在创建一个新的元组,并且将旧的元组丢弃。

元组通常用于将值打包或拆包到变量中。请尝试以下操作:

>>> name, shares, price = t
>>> name
'AA'
>>> shares
75
>>> price
32.2
>>>

取上面的变量并将其打包回元组中:

>>> t = (name, 2*shares, price)
>>> t
('AA', 150, 32.2)
>>>

练习 2.2:把字典当作数据结构

可以创建字典来替代元组。

>>> d = {
'name' : row[0],
'shares' : int(row[1]),
'price' : float(row[2])
}
>>> d
{'name': 'AA', 'shares': 100, 'price': 32.2 }
>>>

计算持有的总价:

>>> cost = d['shares'] * d['price']
>>> cost
3220.0000000000005
>>>

将此示例与上面涉及元组的相同的计算进行比较,将股份数目修改为 75。

>>> d['shares'] = 75
>>> d
{'name': 'AA', 'shares': 75, 'price': 32.2 }
>>>

与元组不同,字典可以自由修改。添加一些属性:

>>> d['date'] = (6, 11, 2007)
>>> d['account'] = 12345
>>> d
{'name': 'AA', 'shares': 75, 'price':32.2, 'date': (6, 11, 2007), 'account': 12345}
>>>

练习 2.3: 字典的其它操作

如果将一个字典转换为列表,则将获得其所有的键:

>>> list(d)
['name', 'shares', 'price', 'date', 'account']
>>>

类似地,如果使用 for 语句对字典进行迭代,则将获得其所有的键。

>>> for k in d:
print('k =', k) k = name
k = shares
k = price
k = date
k = account
>>>

尝试使用这个同时执行查找的变体:

>>> for k in d:
print(k, '=', d[k]) name = AA
shares = 75
price = 32.2
date = (6, 11, 2007)
account = 12345
>>>

也可以使用 keys() 方法获得所有的键:

>>> keys = d.keys()
>>> keys
dict_keys(['name', 'shares', 'price', 'date', 'account'])
>>>

在这里,keys() 稍微有点不同,它返回的是一个 dict_keys 对象。

这是对原始字典的覆盖,它始终提供当前字典的键——即使字典改变了。例如,试试一下操作:

>>> del d['account']
>>> keys
dict_keys(['name', 'shares', 'price', 'date'])
>>>

请注意,尽管没有再次调用 d.keys() ,但键'account' 消失了。

一个更优雅地一起使用键和值的方式是使用 items() 方法。这可以获得键值组成的元组 (key, value)

>>> items = d.items()
>>> items
dict_items([('name', 'AA'), ('shares', 75), ('price', 32.2), ('date', (6, 11, 2007))])
>>> for k, v in d.items():
print(k, '=', v) name = AA
shares = 75
price = 32.2
date = (6, 11, 2007)
>>>

如果有类似于 items 的元组,那么可以使用 dict() 函数创建一个字典。请尝试以下操作:

>>> items
dict_items([('name', 'AA'), ('shares', 75), ('price', 32.2), ('date', (6, 11, 2007))])
>>> d = dict(items)
>>> d
{'name': 'AA', 'shares': 75, 'price':32.2, 'date': (6, 11, 2007)}
>>>

目录 | 上一节 (1.7 函数) | 下一节 (2.2 容器)

注:完整翻译见 https://github.com/codists/practical-python-zh

翻译:《实用的Python编程》02_01_Datatypes的更多相关文章

  1. 翻译:《实用的Python编程》InstructorNotes

    实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...

  2. 翻译:《实用的Python编程》README

    欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...

  3. 翻译:《实用的Python编程》05_02_Classes_encapsulation

    目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...

  4. 翻译:《实用的Python编程》04_02_Inheritance

    目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...

  5. 翻译:《实用的Python编程》01_02_Hello_world

    目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...

  6. 翻译:《实用的Python编程》03_03_Error_checking

    目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...

  7. 翻译:《实用的Python编程》03_04_Modules

    目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...

  8. 翻译:《实用的Python编程》03_05_Main_module

    目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...

  9. 翻译:《实用的Python编程》04_01_Class

    目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...

随机推荐

  1. 全局负载均衡与CDN内容分发

    CDN简介 CDN的全称是Content Delivery Network,即内容分发网络.CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡.内容分发. ...

  2. JavaScript解构赋值

    JavaScript解构赋值 JavaScript解构赋值为我们提供了很多方便,但是用法比较多,本文就来梳理一下.总体来说,主要就两种地方使用解构赋值,一种是数组的解构赋值,另一种是对象的解构赋值.以 ...

  3. pytest测试框架+jenkins结合pytest+jenkins邮件通知配置

    刚刚做完一个项目,由于这是一个方案项目,而不是产品,所以各种准备很不充分,很多公司的能力不能复用,整个团队又都是新员工,而且有部分实习生,匆忙上马,今天对我的自动化框架做一个回溯 自动化测试框架的选择 ...

  4. 2021年的十五个DevOps趋势预测

    DevOps已经走过了很长的一段路,毫无疑问,它将在今年继续闪耀.由于许多公司都在寻找围绕其数字化转型的最佳实践,因此了解领导者认为该行业的发展方向非常重要.从这个意义上说,下面的文章收集了DevOp ...

  5. BZOJ2555 SubString【SAM + Link Cut Tree】

    BZOJ2555. SubString 要求在线询问一个串在原串中出现的次数,并且可以在原串末尾添加字符串 如果没有修改的话,考虑建出\(parent\)树之后统计每个\(endpos\)节点的\(r ...

  6. 【noi 2.7_2987】小兔子捡金币(算法效率)

    题意:问蛇形回文的访问次序. 解法:很基础的一道题,先算出询问的点处在第几环,再用4个while一个个走一遍这一圈.P.S.我一直想办法想用不用while(),可是真的一直WA!所以用while()既 ...

  7. Java 窗口 小马时钟

    写在前面: eclipse爽到 好多都是抄的,记不住原网址了 摸爆了 搞了一个无边框JFrame,给JFrame加入鼠标监听器实现了拖动 搞了按钮,可以关闭.最小化.始终显示在前.静音 icon是抄( ...

  8. AtCoder Beginner Contest 181 E - Transformable Teacher (贪心,二分)

    题意:有一长度为奇数\(n\)的数组\(a\),和长度为\(m\)的数组\(b\),现要求从\(b\)中选择一个数放到\(a\)中,并将\(a\)分成\((n+1)/2\)个数对,求最小的所有数对差的 ...

  9. - Visible Trees HDU - 2841 容斥原理

    题意: 给你一个n*m的矩形,在1到m行,和1到n列上都有一棵树,问你站在(0,0)位置能看到多少棵树 题解: 用(x,y)表示某棵树的位置,那么只要x与y互质,那么这棵树就能被看到.不互质的话说明前 ...

  10. Shell 函数 & 数组

    Shell 函数 函数介绍 # 什么是函数? 具备某一功能的工具 => 函数 事先准备工具的过程 => 函数的定义 遇到应用场景拿来就用 => 函数的调用 # 为何要用函数? 没有引 ...