数据类型和数据结构

PythonPythonBeginner
立即练习

This tutorial is from open-source community. Access the source code

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

本节介绍以元组和字典形式存在的数据结构。

基本数据类型

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

特殊情况(零元组、一元组)。

t = ()            ## 一个空元组
w = ('GOOG', )    ## 一个包含一项的元组

元组通常用于表示 简单的 记录或结构。通常,它是一个由多个部分组成的单个 对象。一个很好的类比是:元组就像数据库表中的一行。

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

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])

元组打包

元组更多的是将相关的项打包成一个单一的 实体

s = ('GOOG', 100, 490.1)

然后,这个元组作为一个单一对象很容易传递到程序的其他部分。

元组解包

要在其他地方使用元组,你可以将其各个部分解包到变量中。

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

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

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

元组与列表

元组看起来像是只读列表。然而,元组最常用于由多个部分组成的 单个项。列表通常是不同项的集合,通常所有项都属于同一类型。

record = ('GOOG', 100, 490.1)       ## 一个表示投资组合中一条记录的元组

symbols = [ 'GOOG', 'AAPL', 'IBM' ]  ## 一个表示三个股票代码的列表

字典

字典是键到值的映射。它有时也被称为哈希表或关联数组。键用作访问值的索引。

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']
## 对比
s[2]

在前几个练习中,你编写了一个程序来读取数据文件 portfolio.csv。使用 csv 模块,可以很容易地逐行读取文件。

>>> import csv
>>> f = open('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' 也从 keys 中消失了。

一种更优雅的同时处理键和值的方法是使用 items() 方法。这会给你 (键, 值) 元组:

>>> 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)}
>>>

总结

恭喜你!你已经完成了数据类型和数据结构实验。你可以在LabEx中练习更多实验来提升你的技能。