01. Python3 数据类型(一)
python3 基本数据类型大致分为 可变数据类型 和 不可变数据类型 ,不可变有 Number(数字)、String(字符串)、Tuple(元组) ,可变有 List(列表)Dictionary(字典)Set(集合)
文章目录
- 01. Python3 数据类型(一)
- Number
- 关于Number一些运算和内置函数
- 字符串
- 索引 (通用序列操作)
- 切片[左索引:右索引:步长] (通用序列操作)
- 常用方法
- 格式化方法format()
- List(列表)
- 序列 UML
- 检验值是否存在序列中 (通用序列操作)
- 序列的*
- 序列的+ 和 +=
- 常用方法
- 列表推导式和生成器表达式
- 易读性
- 生成器表达式
- 列表推导同filter和map的比较
- 切片
- 对对象进行切片
- 切片原理
- 给切片赋值
- 元组
- 元组拆包
- 具名元组
- 字典
- Map UML
- 字典推导
- dict、 defaultdict 和 OrderedDict
- 用setdefault处理找不到的键 (用于更新操作)
- 映射的弹性键查询
- 字典的变种
- 不可变映射类型
- 集合
- 集合 UML
- set 和 frozenset
- 集合字面量
- 集合推导
- 集合的操作
- dict和set的背后
- dict的实现及其导致的结果
- set的实现以及导致的结果
Number
python3 支持int、float、bool、complex(复数)
关于Number一些运算和内置函数
# /运算结果为小数
print
(
4
/
2
)
# ==>2.0
print
(
5
/
4
)
# ==>1.25
# //运算结果舍弃余数
print
(
9.0
//
4
)
# ==>2.0
print
(
9.2
//
4
)
# ==>2.0
print
(
9
//
4
)
# ==>2
# %运算保留余数
print
(
9.0
%
4
)
# ==>1.0
print
(
9.2
%
4
)
# ==>1.1999999999999993
print
(
9
%
4
)
# ==>1
# **运算为幂运算
print
(
2
**
-
1
)
# ==>0.5
# 绝对值abs(Number)
print
(
abs
(
-
3
)
)
# ==>3
# 向原点0取整
print
(
int
(
-
2.3
)
)
# ==>-2
# 四舍五入round(no_complex, 0)
print
(
round
(
3.1415
,
3
)
)
# ==>3.142
字符串
字符串是以单引号’或双引号"括起来的任意文本,比如’abc’,"xyz"等等。
索引 (通用序列操作)
# 正索引
print
(
"abcde"
[
0
]
)
# ==>a
# 负索引,-n:倒数第n个元素
print
(
"abcde"
[
-
1
]
)
# ==>e
切片[左索引:右索引:步长] (通用序列操作)
# 切片str[m:n],m默认为0,n默认为len(str)
print
(
"abcde"
[
1
:
3
]
)
# ==>bc
print
(
"abcde"
[
1
:
]
)
# ==>bcde
print
(
"abcde"
[
:
2
]
)
# ==>ab
print
(
'abcde'
[
0
:
5
:
2
]
)
# ==>ace
常用方法
# 大小写转换
print
(
"Python"
.
upper
(
)
)
# ==>PYTHON
print
(
"Python"
.
lower
(
)
)
# ==>python
print
(
"hello world"
.
capitalize
(
)
)
# ==>Hello world
print
(
"hello world"
.
title
(
)
)
# ==>Hello World
# 非重叠字符串的数量
print
(
"ablllabllab"
.
count
(
"ll"
)
)
# ==>2
# 字符串查找
print
(
"hihihi"
.
find
(
"hi"
)
)
# ==>0
print
(
"hihihi"
.
rfind
(
"hi"
)
)
# ==>4
# 字符串替换所有
print
(
"To a To b"
.
replace
(
'To'
,
'to'
)
)
# ==>to a to b
# 首尾去除,默认空格
print
(
"first-first-end"
.
strip
(
'find'
)
)
# ==>rst-first-e
print
(
"first-first-end"
.
strip
(
'first-'
)
)
# ==>end
print
(
"first-end-end"
.
rstrip
(
'-end'
)
)
# ==>first
# 用于输出的对齐,默认填充空格
print
(
"hello"
.
ljust
(
9
,
'-'
)
+
"hello"
.
center
(
9
,
'+'
)
+
"hello"
.
rjust
(
9
,
'-'
)
)
# ==>hello----++hello++----hello
格式化方法format()
List(列表)
列表是一种序列,因此索引和切片与字符串类似
序列 UML
检验值是否存在序列中 (通用序列操作)
print
(
2
in
[
1
,
2
,
3
,
4
]
)
# ==>True
print
(
'bc'
in
'abcde'
)
# ==>True
序列的*
# 一个包含 3 个列表的列表, 嵌套的 3 个列表各自有 3 个元素来代表井字游戏的一行方块
board
=
[
[
'_'
]
*
3
for
i
in
range
(
3
)
]
# [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
board
[
1
]
[
2
]
=
'X'
# [['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]
# *的一个特性,复制的是引用
weird_board
=
[
[
'_'
]
*
3
]
*
3
# [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
weird_board
[
1
]
[
2
]
=
'O'
# [['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]
序列的+ 和 +=
l1
=
[
1
,
2
,
3
,
4
,
5
]
l2
=
l1
# +操作是先将[1, 2, 3, 4, 5]和[6, 7, 8]相加,将结果的引用赋值给l2
l2
=
l2
+
[
6
,
7
,
8
]
print
(
l1
)
# ==>[1, 2, 3, 4, 5]
print
(
l2
)
# ==>[1, 2, 3, 4, 5, 6, 7, 8]
print
(
id
(
l1
)
==
id
(
l2
)
)
# ==>False
l1
=
[
1
,
2
,
3
,
4
,
5
]
l2
=
l1
# +=操作是就地在l2后面加[6, 7, 8]
l2
+=
[
6
,
7
,
8
]
print
(
l1
)
# ==>[1, 2, 3, 4, 5, 6, 7, 8]
print
(
l2
)
# ==>[1, 2, 3, 4, 5, 6, 7, 8]
print
(
id
(
l1
)
==
id
(
l2
)
)
# ==>True
常用方法
l1
=
[
'1'
,
'2'
,
'3'
,
'4'
,
'1'
]
l1
.
count
(
'1'
)
# 统计某个元素出现的次数
l1
.
index
(
'1'
)
# 找出某个元素的第一次出现的位置
l1
.
reverse
(
)
# 将元素进行反转
l1
.
sort
(
reverse
=
True
)
# 对列表进行反向排序,列表被改变,返回值是空
l1
.
append
(
'5'
)
# 向末尾添加一个元素
l1
.
insert
(
3
,
'6'
)
# 向指定索引位置添加一个元素
l1
.
extend
(
[
'1'
,
'2'
]
)
# 向末尾添加,等同于 li + ['1','2']
l1
.
pop
(
3
)
# 删除指定索引位置的一个元素,默认末尾
l1
.
remove
(
'1'
)
# 移除列表中的第一个匹配元素
l1
.
clear
(
)
# 清空所有元素
# split和join
print
(
"1,2,3,4,5"
.
split
(
','
)
)
# ==>['1', '2', '3', '4', '5']
# join参数中的元素不能为数字
print
(
","
.
join
(
[
'1'
,
'2'
,
'3'
,
'4'
,
'5'
]
)
)
# ==>1,2,3,4,5
列表推导式和生成器表达式
列表推导是构建列表(list) 的快捷方式, 而生成器表达式则可以用来创建其他任何类型的序列。 如果你的代码里并不经常使用它们, 那么很可能你错过了许多写出可读性更好且更高效的代码的机会。
易读性
# 把一个字符串变成 Unicode 码位的列表
symbols
=
'$¢£¥€¤'
code
=
[
]
for
symbol
in
symblos
:
codes
.
append
(
ord
(
symbol
)
)
# 等价于
symbols
=
'$¢£¥€¤'
code
=
[
ord
(
symbol
)
for
symbol
in
symbols
]
生成器表达式
虽然也可以用列表推导来初始化元组、 数组或其他序列类型, 但是生成器表达式是更好的选择。 这是因为生成器表达式背后遵守了迭代器协议, 可以逐个地产出元素, 而不是先建立一个完整的列表, 然后再把这个列表传递到某个构造函数里。 前面那种方式显然能够节省内存。
生成器表达式的语法跟列表推导差不多, 只不过把方括号换成圆括号而已。
symbols
=
'$¢£¥€¤'
tuple
(
ord
(
symbol
)
for
symbol
in
symbols
)
[
ord
(
symbol
)
for
symbol
in
symbols
if
ord
(
symbol
)
>
0
]
列表推导同filter和map的比较
filter 和 map 合起来能做的事情, 列表推导也可以做, 而且还不需要借助难以理解和阅读的 lambda 表达式。
# 选出所有大于127的Unicode 码位
symbols
=
'$¢£¥€¤'
beyond_ascii
=
list
(
filter
(
lambda
c
:
c
>
127
,
map
(
ord
,
symbols
)
)
)
# beyond_ascii==>[162, 163, 165, 8364, 164]
# 等价于
beyond_ascii
=
[
ord
(
s
)
for
s
in
symbols
if
ord
(
s
)
>
127
]
切片
像列表(list)、元组(tuple) 和字符串(str) 这类序列类型都支持切片操作, 但是实际上切片操作比人们所想象的要强大很多。
对对象进行切片
用
s[a:b:c]
的形式对 s 在 a 和 b之间以 c 为间隔取值。 c 的值还可以为负, 负值意味着反向取值。
s
=
'bicycle'
print
(
s
[
:
:
3
]
)
# ==>bye
print
(
s
[
:
:
-
1
]
)
# ==>elcycib
print
(
s
[
:
:
-
2
]
)
# ==>eccb
切片原理
a:b:c
这种用法只能作为索引或者下标用在 [] 中来返回一个切片对象: slice(a, b, c)。
对
seq[start:stop:step]
进行求值的时候, Python 会调用
seq.__getitem__(slice(start, stop, step))
。
class
MySeq
:
def
__getitem__
(
self
,
index
)
:
return
index
s
=
MySeq
(
)
print
(
s
[
1
]
)
# ==>1
print
(
s
[
1
:
4
]
)
# ==>slice(1, 4, None)
print
(
s
[
1
:
4
:
2
,
7
:
9
]
)
# ==>(slice(1, 4, 2), slice(7, 9, None))
通过审查 slice 它有 start、 stop 和 step 数据属性, 以及indices 方法。
# S.indices(len) -> (start, stop, stride)
# 给定长度为 len 的序列, 计算 S 表示的扩展切片的起始(start)和结尾(stop) 索引, 以及步幅(stride) 。 超出边界的索引会被截掉, 这与常规切片的处理方式一样。
# 假设有个长度为 5 的序列, 例如 'ABCDE'
# 'ABCDE'[:10:2] 等同于 'ABCDE'[0:5:2]
slice
(
None
,
10
,
2
)
.
indices
(
5
)
# ==>(0, 5, 2)
# 'ABCDE'[-3:] 等同于 'ABCDE'[2:5:1]
slice
(
-
3
,
None
,
None
)
.
indices
(
5
)
# ==>(2, 5, 1)
给切片赋值
如果把切片放在赋值语句的左边, 或把它作为 del 操作的对象, 我们就可以对序列进行嫁接、 切除或就地修改操作。
ps : 赋值语句右边必须是一个可迭代对象
l
=
list
(
range
(
10
)
)
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
l
[
2
:
5
]
=
[
20
,
30
]
# [0, 1, 20, 30, 5, 6, 7, 8, 9]
del
l
[
5
:
7
]
# [0, 1, 20, 30, 5, 8, 9]
l
[
3
:
:
2
]
=
[
11
,
22
]
# [0, 1, 20, 11, 5, 22, 9]
元组
元组除了用作不可变的列表, 它还可以用于没有字段名的记录。
# 经纬度
lax_coordinates
=
(
33.9425
,
-
118.408056
)
# 东京市的一些信息:市名、年份、人口(单位: 百万)、人口变化(单位: 百分比)和面积(单位: 平方千米)。
city
,
year
,
pop
,
chg
,
area
=
(
'Tokyo'
,
2003
,
32450
,
0.66
,
8014
)
元组拆包
# 简单的拆包
lax_coordinates
=
(
33.9425
,
-
118.408056
)
latitude
,
longitude
=
lax_coordinates
# 元组拆包
# 交换变量的值
a
,
b
=
1
,
2
a
,
b
=
b
,
a
# 用 * 运算符把一个可迭代对象拆开作为函数的参数
# 20除8 等于 2余4
print
(
divmod
(
20
,
8
)
)
#==>(2, 4)
t
=
(
20
,
8
)
print
(
divmod
(
*
t
)
)
#==>(2, 4)
# 使用_占位符
_
,
b
=
(
1
,
2
)
print
(
b
)
#==>2
# Python3 在平行赋值中使用*args
a
,
b
,
*
rest
=
range
(
5
)
# (0, 1, [2, 3, 4])
a
,
b
,
*
rest
=
range
(
3
)
# (0, 1, [2])
a
,
b
,
*
rest
=
range
(
2
)
# (0, 1, [])
a
,
*
body
,
c
,
d
=
range
(
5
)
# (0, [1, 2], 3, 4)
*
head
,
b
,
c
,
d
=
range
(
5
)
# ([0, 1], 2, 3, 4)
# 嵌套元组拆包
(
a
,
b
,
(
c
,
d
)
)
=
(
1
,
2
,
(
3
,
4
)
)
具名元组
collections.namedtuple 是一个工厂函数, 它可以用来构建一个带字段名的元组和一个有名字的类——这个带名字的类对调试程序有很大帮助
from
collections
import
namedtuple
City
=
namedtuple
(
'City'
,
'name country population coordinates'
)
tokyo
=
City
(
'Tokyo'
,
'JP'
,
36.933
,
(
35.689722
,
139.691667
)
)
print
(
tokyo
)
# ==>City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722,139.691667))
print
(
tokyo
.
population
)
# ==>36.933
print
(
tokyo
.
coordinates
)
# ==>(35.689722, 139.691667)
字典
字典这个数据结构活跃在所有 Python 程序的背后, 即便你的源码里并没有直接用到它。
Map UML
字典推导
字典推导(dictcomp) 可以从任何以键值对作为元素的可迭代对象中构建出字典。
DIAL_CODES
=
[
(
86
,
'China'
)
,
(
91
,
'India'
)
,
(
1
,
'United States'
)
,
(
62
,
'Indonesia'
)
,
(
55
,
'Brazil'
)
,
(
92
,
'Pakistan'
)
,
(
880
,
'Bangladesh'
)
,
(
234
,
'Nigeria'
)
,
(
7
,
'Russia'
)
,
(
81
,
'Japan'
)
,
]
country_code
=
{
country
:
code
for
code
,
country
in
DIAL_CODES
}
# {'China': 86, 'India': 91, 'Bangladesh': 880, 'United States': 1,'Pakistan': 92,'Japan': 81, 'Russia': 7, 'Brazil': 55, 'Nigeria':234, 'Indonesia': 62}
x
=
{
code
:
country
.
upper
(
)
for
country
,
code
in
country_code
.
items
(
)
if
code
<
66
}
# {1: 'UNITED STATES', 55: 'BRAZIL', 62: 'INDONESIA', 7: 'RUSSIA'}
dict、 defaultdict 和 OrderedDict
后面两个数据类型是 dict 的变种, 位于 collections 模块内。
不同方法
dict | defaultdict | OrderedDict | 说明 | |
---|---|---|---|---|
d.__copy__()
|
√ |
用于支持
copy.copy
|
||
d.default_factory
|
√ |
在
__missing__
函数中被调用的函数, 用以给未找到的元素设置值
|
||
d.__missing__(k)
|
√ |
当
__getitem__
找不到对应键的时候, 这个方法会被调用
|
||
d.move_to_end(k,[last])
|
√ | 把键为 k 的元素移动到最靠前或者最靠后的位置(last 的默认值是 True) | ||
d.__reversed__()
|
√ | 返回倒序的键的迭代器 |
- default_factory 并不是一个方法, 而是一个可调用对象( callable) , 它的值在defaultdict 初始化的时候由用户设定。
相同方法
方法 | 说明 |
---|---|
d.clear()
|
移除所有元素 |
d.__contains__(k)
|
检查 k 是否在 d 中 |
d.copy()
|
浅复制 |
d.fromkeys(it,[initial])
|
将迭代器 it 里的元素设置为映射里的键, 如果有 initial 参数,
就把它作为这些键对应的值(默认是 None) |
d.get(k,[default])
|
没有键 k, 则返回 None 或者default |
d.items()
|
返回 d 里所有的键值对 |
d.__iter__()
|
获取键的迭代器 |
d.keys()
|
获取所有的键 |
d.pop(k, [defaul])
|
返回键 k 所对应的值, 然后移除这个键值对。 如果没有这
个键,返回 None 或者 defaul |
d.popitem()
|
随机返回一个键值对并从字典里移除它 |
d.setdefault(k,[default])
|
若字典里有键k, 则把它对应的值设置为 default,
然后返回这个值; 若无, 则让 d[k] =default, 然后返回 default |
d.update(m,[**kargs])
|
m 可以是映射或者键值对迭代器, 用来更新 d 里对应的条目 |
d.values()
|
返回字典里的所有值 |
- OrderedDict.popitem() 会移除字典里最先插入的元素( 先进先出) ; 同时这个方法还有一个可选的 last 参数, 若为真, 则会移除最后插入的元素( 后进先出) 。
用setdefault处理找不到的键 (用于更新操作)
当字典 d[k] 不能找到正确的键的时候, Python 会抛出异常, 这个行为符合 Python 所信奉的“快速失败”哲学。
my_dict
.
setdefault
(
key
,
[
]
)
.
append
(
new_value
)
# 跟这样写
if
key
not
in
my_dict
:
my_dict
[
key
]
=
[
]
my_dict
[
key
]
.
append
(
new_value
)
#二者的效果是一样的,只不过后者至少要进行两次键查询——如果键不存在的话,就是三次,用 setdefault 只需要一次就可以完成整个操作
映射的弹性键查询
有时候为了方便起见, 就算某个键在映射里不存在, 我们也希望在通过这个键读取值的时候能得到一个默认值。
**defaultdict: 处理找不到的键的一个选择 **
在用户创建 defaultdict 对象的时候, 就需要给它配置一个为找不到的键创造默认值的方法。
'''
比如,我们新建了这样一个字典:dd = defaultdict(list),如果键'new-key' 在 dd 中还不存在的话,表达式 dd['new-key'] 会按照以下的步骤来行事。
(1) 调用 list() 来建立一个新列表。
(2) 把这个新列表作为值, 'new-key' 作为它的键, 放到 dd 中。
(3) 返回这个列表的引用。
而这个用来生成默认值的可调用对象存放在名为 default_factory 的实例属性里。
'''
d
=
collections
.
defaultdict
(
list
)
print
(
d
.
default_factory
)
# ==>
print
(
d
[
'key'
]
)
# ==>[]
-
defaultdict 里的
default_factory
只会在__getitem__
里被调用,get(key)
则会返回 None。
特殊方法
__missing__
所有的映射类型在处理找不到的键的时候, 都会牵扯到
__missing__
方法。 这也是这个方法称作“missing”的原因。 虽然基类 dict 并没有定义这个方法, 但是 dict 是知道有这么个东西存在的。
# 在查询的时候把非字符串的键转换为字符串
class
StrKeyDict0
(
dict
)
:
def
__missing__
(
self
,
key
)
:
if
isinstance
(
key
,
str
)
:
raise
KeyError
(
key
)
return
self
[
str
(
key
)
]
def
get
(
self
,
key
,
default
=
None
)
:
try
:
return
self
[
key
]
except
KeyError
:
return
default
def
__contains__
(
self
,
key
)
:
return
key
in
self
.
keys
(
)
or
str
(
key
)
in
self
.
keys
(
)
-
__missing__
方法只会被__getitem__
调用(比如在表达式 d[k] 中) -
像
k in my_dict.keys()
这种操作在 Python 3 中是很快的, 而且即便映射类型对象很庞大也没关系。 这是因为dict.keys()
的返回值是一个“视图”。 视图就像一个集合, 而且跟字典类似的是, 在视图里查找一个元素的速度很快。
字典的变种
**collections.OrderedDict **
这个类型在添加键的时候会保持顺序, 因此键的迭代次序总是一致的。 OrderedDict 的
popitem
方法默认删除并返回的是字典里的最后一个元素, 但是如果像
my_odict.popitem(last=False)
这样调用它, 那么它删除并返回第一个被添加进去的元素。
**collections.ChainMap **
该类型可以容纳数个不同的映射对象, 然后在进行键查找操作的时候, 这些对象会被当作一个整体被逐个查找, 直到键被找到为止。 这个功能在给有嵌套作用域的语言做解释器的时候很有用, 可以用一个映射对象来代表一个作用域的上下文。
**collections.Counter **
这个映射类型会给键准备一个整数计数器。 每次更新一个键的时候都会增加这个计数器。 所以这个类型可以用来给可散列表对象计数, 或者是当成多重集来用——多重集合就是集合里的元素可以出现不止一次。 Counter 实现了 + 和 - 运算符用来合并记录, 还有像
most_common([n])
这类很有用的方法。
ct
=
collections
.
Counter
(
'abracadabra'
)
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
ct
.
update
(
'aaaaazzz'
)
# Counter({'a': 10, 'z': 3, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
ct
.
most_common
(
2
)
# [('a', 10), ('z', 3)]
**colllections.UserDict **
这个类其实就是把标准 dict 用纯 Python 又实现了一遍。跟 OrderedDict、 ChainMap 和 Counter 这些开箱即用的类型不同, UserDict 是让用户继承写子类的。而更倾向于从 UserDict 而不是从 dict 继承的主要原因是, 后者有时会在某些方法的实现上走一些捷径, 导致我们不得不在它的子类中重写这些方法, 但是 UserDict 就不会带来这些问题。
不可变映射类型
从 Python 3.3 开始, types 模块中引入了一个封装类名叫MappingProxyType。 如果给这个类一个映射, 它会返回一个只读的映射视图。 虽然是个只读视图, 但是它是动态的。 这意味着如果对原映射做出了改动, 我们通过这个视图可以观察到, 但是无法通过这个视图对原映射做出修改。
d
=
{
1
:
'A'
}
d_proxy
=
MappingProxyType
(
d
)
print
(
d_proxy
[
1
]
)
# ==>A
# 不可修改
d_proxy
[
2
]
=
'x'
# TypeError: 'mappingproxy' object does not support item assignment
集合
“集”这个概念在 Python 中算是比较年轻的, 同时它的使用率也比较低。
集合 UML
set 和 frozenset
-
set无序排序且不重复,是可变的,有add(),remove()等方法。
-
frozenset是冻结的集合,它是不可变的,存在哈希值,好处是它可以作为字典的key,也可以作为其它集合的元素。缺点是一旦创建便不能更改,没有add,remove方法。
集合字面量
除空集之外, 集合的字面量——{1}、 {1, 2}, 等等——看起来跟它的数学形式一模一样。 如果是空集, 那么必须写成 set() 的形式。
- 只是写成 {} 的形式, 跟以前一样, 你创建的其实是个空字典。
集合推导
s
=
{
chr
(
i
)
for
i
in
range
(
32
,
40
)
}
# {'!', '#', '"', '$', '%', "'", ' ', '&'}
集合的操作
集合的数学运算 :
数学符号 | Python运算符 | 描述 |
---|---|---|
S ∩ Z | s & z | s 和 z 的交集 |
S ∪ Z | s | z | s 和 z 的并集 |
S - Z | s - z | s 和 z 的差集, 或者叫作相对补集 |
S △ Z | s ^ z | s 和 z 的对称差集 |
集合的比较运算符 :
数学符号 | Python运算符 | 描述 |
---|---|---|
e ∈ S | e in s | 元素 e 是否属于 s |
S ⊆ Z | s <= z | s 是否为 z 的子集 |
S ⊂ Z | s < z | s 是否为 z 的真子集 |
S ⊇ Z | s >= z | s 是否为 z 的父集 |
S ⊃ Z | s > z | s 是否为 z 的真父集 |
dict和set的背后
想要理解 Python 里字典和集合类型的长处和弱点, 它们背后的散列表是绕不开的一环。
dict的实现及其导致的结果
-
键必须是可散列的
-
支持
hash()
函数, 并且通过__hash__()
方法所得到的散列值是不变的。 -
支持通过
__eq__()
方法来检测相等性。 -
若
a == b
为真, 则hash(a) == hash(b)
也为真。
-
支持
-
字典在内存上的开销巨大
- 字典使用了散列表, 而散列表又必须是稀疏的, 这导致它在空间上的效率低下。
- 如果需要存放数量巨大的记录, 那么放在由元组或是具名元组构成的列表中会是比较好的选择 。
-
键查询很快
- dict 的实现是典型的空间换时间: 字典类型有着巨大的内存开销, 但它们提供了无视数据量大小的快速访问——只要字典能被装在内存里。
-
键的次序取决于添加顺序
- 往 dict 里添加新键而又发生散列冲突的时候, 新键可能会被安排存放到另一个位置。
-
往字典里添加新键可能会改变已有键的顺序
- 无论何时往字典里添加新的键, Python 解释器都可能做出为字典扩容的决定。 这个过程中可能会发生新的散列冲突, 导致新散列表中键的次序变化。
ps: 在 Python 3 中, .keys()、 .items() 和 .values() 方法返回的都是字典视图。
set的实现以及导致的结果
set 和 frozenset 的实现也依赖散列表, 但在它们的散列表里存放的只有元素的引用 。
上面所提到的字典和散列表的几个特点, 对集合来说几乎都是适用的。
- 集合里的元素必须是可散列的。
- 集合很消耗内存。
- 可以很高效地判断元素是否存在于某个集合。
- 元素的次序取决于被添加到集合里的次序。
- 往集合里添加元素, 可能会改变集合里已有元素的次序。