Python之数据类型
内置类型
数据类型
- 空置:None
- 数字:bool,int,float,long,complex
- 序列:str,unicode,list,tuple
- 字典:dict
- 集合:set, frozenset
数字
bool
None,0,空字符串,空字符串,没有元素的容器类型都可以认为False
反之为True
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
print(list(map(bool, [None, 0, "", u"", list(), tuple(), dict(), set(), frozenset()])))
虽然比较古怪,但是数字的确可以充当bool
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
print(int(True)) # 1
print(int(False)) # 0
print(range(10)[True]) # 1
x = 5
print(range(10)[x > 3]) # 1
int
在64位平台上,int
类型是64位整数,整数是虚拟机特殊照顾对象
- 整数需要在堆上开辟相对应的
PyIntBlock
块来缓存整数 - 在区间为
[-5,256)
之内的小整数会被专门的数据进行缓存 PyIntBlock
块的内存不会被系统回收,直到进程结束
小整数池
Python为了提高效率,使用小整数池子概念,避免整数频繁的被创建销毁影响运行速度
小整数池子对应取件范围为 [-5,256]
处于该取件的整数对象都是提前被创建好的,不会被垃圾回收机制回收,在这个范围内的整数对象都使用同一个内存地址
# 使用交互式环境进行测试
a = 1
b = 1
c = 1
a is b is c
print(id(a),id(b),id(c))
a2 = -6
a3 = -6
a2 is a3
a4 = 257
a5 = 257
a4 is a5
a5 = 256
a6 = 256
a5 is a6
在PyIntBlock
块中的内存只会复用不会被删除,如果持有大量的整数对象将导致内存暴涨,且在资源被释放的时候,内存不会操作系统回收,造成了大量的内存泄露
如果使用 range
函数创建一个大量的数字列表,这就需要大量的 PyIntBlock
块来提供内存,且当资源被释放的时候内存不会被系统回收,如果使用 xrange
函数每次迭代之后数字对象被回收,资源释放,在python2
中使用 range
在 python3
已经使用 xrange
# pip install psutil 用来获取内存 统计数据
# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time : 2021/3/11 下午7:13
# @Author : SR
# @Email : srcoder@163.com
# @File : test.py
# @Software: PyCharm
import gc
import os
import psutil
def test():
x = 0
for i in range(10000000):
x += i
return x
def main():
print(test())
gc.collect()
p = psutil.Process(os.getpid())
print(p.memory_info())
if __name__ == '__main__':
main()
float
在Python中使用双精度的浮点数,不能准确表示十进制的小数值,尤其是四舍五入的 round
的结果
3 / 2 # 在python2中返回整数 在python3中返回浮点数
print(3 * 0.1 == 0.3) # false 由于计算机识别 0 1二进制对小数识别不准确
print(round(2.765, 2))
在 round
函数中如果遇到.5
的情况下如果需要保留的小数尾部是奇数则进行保留如果尾部是偶数则进一
print(round(2.675, 2)) # 2.67
print(round(2.685, 2)) # 2.69
如果确实需要使用精确度此时可以使用 Decimal
来控制运算精度
from decimal import Decimal, ROUND_DOWN, ROUND_UP
print(float('0.1') * 3 == float('0.3')) # False
print(Decimal('0.1') * 3 == Decimal('0.3')) # True
print(Decimal(2.675).quantize(Decimal('0.01'), ROUND_UP)) # quantize控制位数 四舍五入 2.68
print(Decimal(2.675).quantize(Decimal('0.01'), ROUND_DOWN)) # 2.67
字符串
与字符串相关的问题很多,比如池化,编码等,字符串为不可变类型数据,其内部存放的是字符序列或者二进制数据
- 短字符串被存储在
arena
区域,str
,unicode
单字符串被永久缓存 - str没有缓存机制,unicode则保留1024个宽字符长度小于9的复用对象
- 内部包含hash值,str另外有标记判断是否被池化
基本方法
累加累乘
print("a" + "b") # ab
print('a' * 3) # aaa
join
使用拼接符号拼接字符串返回字符串
print('|'.join(['a', 'b', 'c'])) # a|b|c
print(''.join(['a', 'b', 'c'])) # abc
split
按照指定字符分割,返回列表
print('a|b|c'.split('|')) # ['a', 'b', 'c']
splitlines
按照行进行分割,返回列表
print('a\nb\nc\n'.splitlines()) # ['a', 'b', 'c']
print('a\nb\nc\n'.splitlines(True)) # 分割后保留换行符 ['a\n', 'b\n', 'c\n']
startswith&&endswith
判断以什么开头或者结尾,返回布尔值
print('abc'.startswith('a'), 'abc'.endswith('c')) # True True
upper&&lower
将字符串转换成全大写或者全小写
print('Abc'.upper(), 'aBC'.lower()) # ABC abc
find
- 查找字符串是否包含子字符串
- 如果指定开始和结束则查找是否在指定范围内
- 如果包含则返回开始的索引值,反之返回-1
info = 'abcd'
print(info.find('a')) # 0
print(info.find('a', 1)) # -1
print(info.find('b', 1, 3)) # 1
print(info.find('3')) # -1
lstrip&&rstirp&&strip
移除指定字符
print(" abc".lstrip(), "abc ".rstrip(), " abc ".strip()) # abc abc abc
print('abc'.strip('ab')) # c
replace
对字符串进行替换
print('abcabc'.replace('bc', 'BC')) # aBCaBC
format
指明道姓格式化
print('{key} = {value}'.format(key='x', value='100')) # x = 100
索引格式化
print('{0},{1},{0}'.format(1, 2, )) # 1,2,1 按照索引进行格式化
千分位符号
print('{0:,}'.format(1234567)) # 1,234,567
千分位,带小数位
print('{0:.3f}'.format(1234.5678)) # 保留三位小数 1234.568
成员
print('{.platform}'.format(sys)) # linux
字典
print('{0[a]},{1[y]}'.format(dict(a=100, b=10), dict(x=200, y=20))) # 100,20
列表
print('{0[5]}'.format(range(10))) # 5
编码
python2默认使用 ASCII
编码在python3中默认使用 utf-8
编码,在python2中为了编码转换,必须将操作系统编码与字符编码统一
import sys
import locale
print(locale.getdefaultlocale()) # 查看当前系统编码
print(sys.getdefaultencoding()) # 查看python默认编码
str
与 unicode
都提供了encode
和 decode
方法
- encode将默认编码转换成其余编码
- decode将默认或者其余编码转换成unicode编码
格式化
指名道姓
print('%(key)s=%(value)s' % dict(key='a', value=100)) # a=100
对齐
print('[%-10s]' % 'a') # [a ] 左对齐
print('[%10s]' % 'a') # [ a] 右对齐
数字符号
print('%+d,%+d' % (-10, 10)) # -10,+10
填充
print('%010d' % 10) # 0000000010共10位不够的用0填充
小数位
print('%0.2f' % 3.1415936) # 保留两位小数
进制前缀
print('%#x,%#x' % (100, 200)) # 0x64,0xc8
池化
在Python进程中,无数的对象拥有 __name__
与 __doc__
这样的名字,池化有助于减少对象数量和内存消耗,提升性能
- 对于长度为小于5的字符串会被池化
- 如果字符串只包含数字,字母,下划线会被池化
- 当处于同一行不同的变量赋予相同的值,其会指向同一个对象,但是这不是池化现象属于编译器优化
# 长度都为0
a = ''
b = ''
print(id(a)) # 139698457602736
print(id(b)) # 139698457602736
print(a is b) # True
# 长度都为1
a = '1'
b = '1'
print(id(a)) # 140115401485256
print(id(b)) # 140115401485256
print(a is b) # True
# 长度都为5
a10 = 'magic' # 140065932133520
b10 = 'magic' # 140065932133520
print(id(a10))
print(id(b10))
print(a10 is b10) # True
# 长度大于5
a10 = 'magic!'
b10 = 'magic!'
print(id(a10)) # 139871762744128
print(id(b10)) # 139871762744240
print(a10 is b10) # False
# 包含数字 字母 下划线
a = "magic_string1"
b = "magic" + "_" + "string1"
print(id(a))
print(id(b))
print(a is b)
intern
其可以把动态运行的对象进行池化,当池化的函数不在有引用的时候将会被回收
a = "".join(['a', 'b', 'c'])
s = 'abc'
print(id(s)) # 140250387396960
print(id(a)) # 140250387451440
print(s is a) # False
s = "".join(['a','b'])
s is "ab"
intern(s) is "ab" # 在python2中使用intern函数
列表
从功能上看列表类似于一个动态大小顺序容器,能够存储各种元素
- 列表元素和存储元素的指针的数组是分开的两块内存,后者由堆分配
- 列表会动态调整数组的大小,预分配内存多于实际元素数量
列表创建
li = [] # 使用中括号代表列表
print(type(li)) # <class 'list'>
res = ['a', 'b'] + ['a', 'b']
print(res) # ['a', 'b', 'a', 'b']
res1 = ['a', 'b'] * 2 # 上述累加可以更改
print(res1) # ['a', 'b', 'a', 'b']
res = map(bool, ['', None, 0, ()])
print(res) # <map object at 0x7fb80c9a6e48>
print(list(res)) # 将序列或者迭代器转换成列表 [False, False, False, False]
print([i * i for i in range(10)]) # 列表生成式 [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
常见方法
数值更改
l = list('abc')
l[1] = 2
print(l) # ['a', 2, 'c']
切片
- 获取一定范围内的数值返回新的列表
- 如果列表为空则返回新的空列表
l = list(range(10))
print(l[2:5]) # [2, 3, 4]
count
- 获取某元素在列表内的数量
- 如果元素不存在返回0
l = list('abcdabbb')
print(l.count('b')) # 4
print(l.count('h')) # 0
index
- 获取元素在列表内的索引
- 可以指定索引起始位置
- 如果元素不存在则会报错
l = list('abcdabbb')
print(l.index('b')) # 1
print(l.index('b', 3)) # 5 指定起始位置
print(l.index('h'))
sort
- 默认将列表从小到大排序,如果加参数
reverse=True
则从大到小排序 - 如果使用变量接收排序之后的列表接收值为None
import random
l = [
i for i in range(20)
]
random.shuffle(l)
l.sort()
print(l) # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
random.shuffle(l)
l.sort(reverse=True) # 从大到小
print(l) # [19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
random.shuffle(l)
res = l.sort()
print(res) # None
添加元素
append
# 尾部添加元素
l = []
for i in range(5):
l.append(i)
print(l)
insert
- 如果插入的索引大于列表长度则插入末尾
- 如果索引为负数则从后往前插入
# 在指定位置插入元素
l = list('abc')
l.insert(4, 'd') # 末尾插入
print(l) # ['a', 'b', 'c', 'd']
l.insert(-2, 'hh') # 从右往左插入 ['a', 'b', 'hh', 'c', 'd']
print(l)
extend
l = list('abc')
l.extend([i for i in range(3)])
print(l) # ['a', 'b', 'c', 0, 1, 2]
+
+=
extend
append
异同点
相同点
- 都可以进行列表之间元素的添加
- append与extend如果使用新的变量来接受添加之后的列表返回值为None
- +如果使用新的变量来接受添加之后的列表会生成新的变量开辟新的内存地址
不同点
- append原地修改可以添加任何元素,无论添加何种元素都是添加在末尾且只当一个元素
- extend原地只可以添加可迭代对象,待添加的对象有多少个则添加多少,如果添加对象是字典则将字典的
key
添加列表中 - +=原地修改列表只可以进行列表之间的操作
- +非原地修改只能执行列表之间的操作,会拷贝生成新的对象
移除元素
clear
- 删除列表所有元素返回一个空列表
- 如果使用变量接收清除之后的列表获取None
- 清空之后的列表内存地址不变
l = [i for i in range(10)]
res = l.clear()
print(res) # None
print(l) # []
del
- 其不但能删除整个列表也可以根据索引删除列表指定值
- 可以根据区间删除连续的元素
- 删除之后的列表内存地址不变
l = [i for i in range(10)]
del l[0] # [1, 2, 3, 4, 5, 6, 7, 8, 9]删除首位
print(l)
l1 = [i for i in range(10)]
del l1[1:5] # [0, 5, 6, 7, 8, 9]删除区间内元素
print(l1)
pop
- 根据索引删除指定的元素
- 如果不写索引默认删除列表最后一个元素
- 可以使用变量接受删除的元素
- 删除之后列表的内存地址不变
l = [i for i in range(10)]
print(id(l))
res = l.pop(1)
print(res) # 1
print(id(l))
print(l) # [0, 2, 3, 4, 5, 6, 7, 8, 9]
remove
- 删除指定的值
- 如果值不在则会报错
- 使用变量接受移除之后的值为None
- 删除之后列表的内存地址不变
l = [i for i in range(10)]
print(id(l))
res = l.remove(1)
print(res) # None
print(id(l))
print(l) # [0, 2, 3, 4, 5, 6, 7, 8, 9]
元组
元组看上去像列表的只读版本,但是在底层有不同之处
- 只读对象,元组和元素指针是一次性连续分配内存的
- 虚拟机缓存n个小于20的元组复用对象
基本方法
创建元组
a = (4) # 少了逗号就变成普通的括号
print(type(a)) # <class 'int'>
a1 = (4,)
print(type(a1)) # <class 'tuple'>
s = tuple([i for i in range(10)])
print(s) # (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
s1 = (i for i in range(10)) # 元组生成式
print(s1) # <generator object <genexpr> at 0x7f9a5c5cd830>
print(tuple(s1)) # 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
count
- 判断元素在元组中的数量
- 如果元素不存在则返回0
s1 = tuple(
'abcdabcd'
)
print(s1.count('a'))
print(s1.count('h'))
index
- 查找某元素所对应的索引值
- 可以指定元素起始的查询位置
- 如果元素不存在则报错
s1 = tuple(
'abcdabcd'
)
print(s1.index('a')) # 0
print(s1.index('a', 2)) # 4
print(s1.index('h')) # 报错
namedtuple
namedtuple
位于collections
模块内,其可以帮助我们通过属性来访问相应的数据namedtuple
除了使用索引能够访问数据,更能够通过属性来访问数据- 在
namedtuple
中每个元素拥有自己的名字,使数据意义清晰明了
from collections import namedtuple
User = namedtuple('User', ['name', 'age']) # 定义一个namedtuple类型的user类包含name age属性
user = User(name='SR', age=18) # 创建一个对象
# user1 = User._make(['SR', 18]) # 也可以通过列表创建 但是需要使用_make方法
print(user) # User(name='SR', age=18)
print(user.name) # SR
print(user.age) # 18
user = user._replace(age=20) # 使用_replace修改数据
print(user) # User(name='SR', age=20)
user_dict = user._asdict() # 将user对象转换成dict
print(user_dict) # OrderedDict([('name', 'SR'), ('age', 20)])
字典
字典(dict)采用开放地址法的hash表来实现
- 自带元素容量为8的
smalltable
只有超出时才能到堆上额外分配元素表内存 - 虚拟机缓存80个复用对象,但在堆上分配的元素表内存会被释放
- 按需要动态调整容量,扩容或者收缩操作都将重新分配内存,重新hash
- 删除元素不会立刻回收内存
基本操作
字典创建
d = {'name': 'SR'}
print(dict(name='SR', age=20))
print(dict((['a', 1], ['b', 2]))) # 使用两个序列类构造字典 {'a': 1, 'b': 2}
print(dict(zip('ab', range(1, 3)))) # {'a': 1, 'b': 2}
res = dict.fromkeys('abc') # 不传值则value等于None
print(res) # {'a': None, 'b': None, 'c': None}
res1 = dict.fromkeys('ab', 1)
print(res1) # {'a': 1, 'b': 1}
res3 = {
key: value for key, value in zip('abc', range(1, 4))
}
print(res3) # {'a': 1, 'b': 2, 'c': 3}字典生成式
in&¬ in
- 判断某key是否在字典中
- 返回布尔值
d = {
'a': 1,
'b': 2
}
print('b' in d) # True
del
- 删除字典的key/value
- 字典key不存在则报错
d = {
'a': 1,
'b': 2
}
del d['b']
print(d) # {'a': 1}
# 报错
del d['c']
print(d)
update
- 有新的key的时候会直接将字典b更新到字典a中
- 如果字典a与字典b有相同的键,会将原值给覆盖
- 该方法没有返回值
a = {1: 2, 2: 2}
b = {1: 1, 3: 3}
a.update(b)
print(a) # {1: 1, 2: 2, 3: 3} 原值被覆盖 新增增加
c = a.update(b)
print(c) # None无返回值
pop
- 根据字典key弹出字典的value
- 可以使用变量接收弹出的value
- 如果key不存在则会报错
d = {
'a': 1,
'b': 2
}
res = d.pop('b')
print(d)
print(res)
popitem
- 随机弹出字典的key/value
- 如果使用变量接受弹出的值会获取一个元组
d1 = {
'b': 2,
'a': 1,
}
res = d1.popitem()
print(d1) # {'b': 2}
print(res) # ('a', 1)
fromkeys
- 用来创建一个字典第一个参数为一个可迭代对象作为字典的
key
第二个对象作为字典的value
如果不传默认为空 - 当通过一个字典来调用
fromkeys
的时候如果后续使用一定需要复制给变量
d = dict.fromkeys(range(3)) # 无默认值
print(d) # {0: None, 1: None, 2: None}
d1 = dict.fromkeys(range(3), 'hello') # hello 作为默认值
print(d1) # {0: 'hello', 1: 'hello', 2: 'hello'}
d2 = {}
d2.fromkeys(range(3), 'hello')
print(d2) # 此时字典并没有key/value
d3 = d2.fromkeys(range(3), 'hello')
print(d3) # {0: 'hello', 1: 'hello', 2: 'hello'}
返回默认值
get
- 如果字典key不存在且无默认值情况下返回None
- 如果字典key不存在有默认值则返回默认值
d = {
'a': 1,
'b': 2
}
print(d.get('c')) # None
print(d.get('c', 123)) # 123默认值
setdefault
- 如果字典的key不存在则将key添加到字典中并且将默认值设置为value且返回默认值
- 如果字典的key存在则直接返回value
d = {
'a': 1,
'b': 2
}
res = d.setdefault('a', 3) # key存在无任何操作
res1 = d.setdefault('c', 3) # key
print(d) # {'a': 1, 'b': 2, 'c': 3}
print(res) # 1
print(res1) # 3
迭代器操作
keys
- 获取字典的所有key
- 返回非常规列表如果需要常规列表需要
list
转换
d = {
'a': 1,
'b': 2
}
res = d.keys()
print(res) # dict_keys(['a', 'b'])
print(type(res)) # <class 'dict_keys'>
print(list(res)) # ['a', 'b']
values
- 获取字典所有的value
- 返回非常规列表如果需要常规列表需要
list
转换
d = {
'a': 1,
'b': 2
}
res = d.values()
print(res) # dict_keys(['a', 'b'])
print(type(res)) # <class 'dict_values'>
print(list(res)) # ['1', '2']
items
- 获取字典的key/value键值对
- 返回非常规列表如果需要常规列表需要
list
转换 - 在列表中以元组的形式包含key/value值
d = {
'a': 1,
'b': 2
}
res = d.items()
print(res) # dict_keys(['a', 'b'])
print(type(res)) # <class 'dict_items'>
print(list(res)) # [('a', 1), ('b', 2)]
defaultdict
- defaultdict类的初始化函数接受一个类型作为参数,当所访问的键不存在的时候,可以实例化一个值作为默认值
- defaultdict还可以使用任何不带参数的可调用函数,到时候函数的返回值作为默认值
from collections import defaultdict
dd = defaultdict(list) # 实例化一个参数
print(dd) # defaultdict(<class 'list'>, {})
dd['a'].append(1) # key a不存在 自动创建
print(dd) # defaultdict(<class 'list'>, {'a': [1]})
def test():
return 0
d1 = defaultdict(test)
print(d1) # defaultdict(<function test at 0x7fe60a6a4ea0>, {})
d1['aa'] # 自动创建key/value
print(d1) # defaultdict(<function test at 0x7fb12c8b7ea0>, {'aa': 0})
print(d1['aa']) # 0
OrderedDict
- 字典是无顺序的可以通过orderdict排序输出
d = dict()
d['a'] = 1
d['b'] = 2
d['c'] = 3
d['e'] = 4
for k, v in d.items():
print(k, v)
from collections import OrderedDict
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
od['d'] = 4
for k, v in od.items():
print(k, v)
for i in range(4):
print(od.popitem())
集合
集合用来存储无顺序不重复对象,不重复对象除了不是同一对象值也不能一样,集合只能存储可 hash
对象
# 判重公式:
(a is b) or (hash(a) == hash(b) and eq(a, b))
- 集合不是序列类型不能像列表一样进行索引取值
- 也不能类似于字符串或者列表进行切片操作
集合生成方法
s = set('abc')
print(type(s)) # <class 'set'>
s1 = {i for i in 'abc'}
print(type(s1)) # <class 'set'>
常见方法
in&¬ in
- 判断元素是否在集合之内
- 返回布尔值
s = set('abc')
print('b' in s) # True
print('d' not in s) # True
add
- 向集合中添加元素
- 如果使用变量接收添加之后的集合会获取None
s = set('abc')
res = s.add('d')
print(res) # None
print(s)
remove
- 移除指定元素
- 如果值不存在会报错
- 使用变量接受移除之后的值会获得None
- 移除之后集合的内存地址不变
s = set('abc')
print(id(s)) # 140661762360264
res = s.remove('b')
print(s)
print(id(s)) # 140661762360264
print(res) # None
discard
- 如果移除的元素存在就移除
- 移除之后集合的内存地址不变
- 使用变量接收移除之后的值会获得None
s = set('abc')
print(id(s)) # 139763619128264
res = s.discard('a')
print(s)
print(id(s)) # 139763619128264
print(res) # None
集合运算
超集判断
print(set("abcd") >= set("ab")) # True
子集判断
print(set("bc") < set("abcd")) # True
并集
print(set("abcd") | set("cdef")) # {'e', 'c', 'd', 'f', 'b', 'a'}
交集
print(set("abcd") & set("abx")) # {'b', 'a'}
差集
print(set("abcd") - set("ab")) # {'d', 'c'}
对称差集
# 不会同时出现在两个集合中的
print(set("abx") ^ set("aby")) # {'x', 'y'}