Python:内置类型

chapter 2.1

字符串与字节序列

(str,bytes)
  • Python3中只有一种能够保存文本信息的数据类型,就是str(string,字符串,不可变序列)
  • Python2中用str来表示字节字符串
  • Python3中用bytes对象来处理字节字序列(字节字符串,不可变序列)
  • Python3所有被'"'''包裹且没有前缀的值都表示str数据类型,都是Unicode.
  • Python2中,Unicode需要加u’xxx’前缀,Python3向后兼容,仍支持这个前缀
  • Unicode字符串中包含无法用字节表示的抽象文本.Unicode字符串需要被编码为二进制数据,才能在磁盘中保存或在网络中传输

bytes(0~255)之间的整数

print(bytes([100,101,102,103,104]))
b'defgh'

bytes或者bytearray,转化为其他序列类型(list/tuple),也会显示真面目.

list(b'defgh')
[100, 101, 102, 103, 104]
tuple(b'defgh')
(100, 101, 102, 103, 104)
set(b'defgh')
{100, 101, 102, 103, 104}
u'今晚打老虎'
'今晚打老虎'

字符串编码为字节序列

  • str.encode(encoding, errors),encoding默认’utf-8’
  • bytes(source, encoding, errors),source:字符串,encoding:指定编码格式,没有默认参数

字节序列转换为字符串

  • bytes.decode(encoding, errors)

  • str(source, encoding, errors),source:字节序列

字节数组bytearray

bytes和str都是不可变对象(每一次微小的修改,都会创建一个新的实例),bytearray是bytes的可变版本,可以任意修改,可以像操作列表一样(append, pop, insert)

字符串的拼接

str在python中作为一个不可变的对象,任意拼接两个字符串都会创建一个新的字符串对象.

如果用循环来拼接多个字符串:

s = ''
for string in str_list:
    s += string

因为str为不可变的,通过循环拼接多个str是一种效率极低的方法.str.join()是一种快速的方法.

s = ''.join(str_list)

str.join()方法的注意事项:

  • .join()方法虽然很快,但是并不意味着所有的拼接操作都要用到它,仅在拼接很多个字符串时是最佳选择
  • 代码的可读性,可读性是最重要的,拼接较少字符串时’+'更合适
  • Cpython中的constant folding,一些复杂的字面值(较长的字符串)会被转换为更短的形式,例如:'a'+'b'+'c'转换为'abc'

Python : list实现细节

  实际上在CPython中列表是由长度可变的数组实现的.Python中的列表是由对其他对象的引用组成的连续数组.指向这个数组的指针及其长度被保存在一个列表的头部结构中,在python中创建列表时,也就是CPython中创建变长数组时,采用了(exponential over-allocation),所以每次插入或删除某个元素并不会改变数组的大小.

列表操作的平均时间复杂度:

  • 复制 :O(n)
  • 添加元素 :O(1)
  • 插入元素 :O(n)
  • 获取元素 :O(1)
  • 修改元素 :O(1)
  • 删除元素 :O(n)
  • 遍历 :O(n)
  • 获取长度为k的片段 :O(k)
  • 删除片段 :O(n)
  • 修改长度为k的片段 :O(n+k)
  • 列表扩展extend :O(k)
  • 乘以k :O(n*k)
  • 测试元素是否在列表中 :O(n)
  • min()/max() :O(n)
  • 获取列表的长度 :O(1)

列表推导

evens = []
for i in range(100):
    if i % 2 ==0:
        evens.append(i)

在python写这样的代码是很糟糕的,原因如下:

  • 解释器在每次循环中都需要判断列表中的哪一部分需要修改
  • 需要一个计数器来跟踪需要处理的元素
  • append()是一个列表方法,每次便利时需要额外执行一次查询函数
evens = [i for in range(100) if i % 2 == 0]
列表推导和内部数组大小的调整

解释器在对列表推导进行求值过程中并不知道最终容器的大小,因此它是无法为数组预先分配大小的.

zip

zip可对多个长度相等的可迭代对象进行均匀的遍历:

for item in zip([1,2,3],[4,5,6],[7,8,9]):
    print(item)
>>>(1, 4, 7)
>>>(2, 5, 8)
>>>(3, 6, 9)

对zip()函数返回的结果再次调用zip():

for item in zip(*zip([1,2,3],[4,5,6],[7,8,9])):
    print(item)
>>>(1, 2, 3)
>>>(4, 5, 6)
>>>(7, 8, 9)

Sequence Unpacking序列解包

不局限于列表和元组,是用于任意序列类型(字符串.字节序列)

a, b, c = 1, 2, 3
print(a, b, c)
>>>1 2 3

a, b, c = 'edc'
print(a, b, c)
>>>e d c

a, b, c = b'edc'
print(a, b, c)
>>>101 100 99

解包还可以利用*表达式获取单个变量中的多个元素:

a, b, *c = 'abcdefg'
print(a, b, c)
>>>a b ['c', 'd', 'e', 'f', 'g']

a, *b, c = 'abcdefg'
print(a, b, c)
>>>a ['b', 'c', 'd', 'e', 'f'] g

(a, b), (c, d) = (1, 2), (3, 4)
print(a, b, c, d)
>>>1 2 3 4

Dict字典

字典推导析和列表推导具有相同的优点 :
squares_dict = {num : num**2 for num in range(100)}

Python3中keys(),values(),items()返回的是视图对象 :

  • keys()
  • values()
  • items(),返回(key,value)二元元组.
    试图对象既有列表的特性也有迭代器的特性,不会将所有的元素都加载到内存中,但是仍可以使用len()来获取它的长度,in判断元素是否在其中.
实现细节

  Cpython使用伪随机探测(pseudo-random probing)的散列表(hash table)作为字典的底层数据结构.所以只有可hash的对象才能作为字典的键值,如果一个对象有个在整个生命周期都不变的散列值hash value,而且这个值可以和其他对象进行比较,那么这个对象就是可hash的.Python中所有的不可变类型都是可hash的,列表,字典,集合是不可hash的.

定义hash类型的协议包括下面这两种方法:

  • hash:给出字典内部实现需要的散列值(int),用户自定义类的实例对象,有id()给出.
  • eq:比较两个对象的值是否相等,对于用户自定义的类,除了自身,所有的实例对象默认不相等.
>>> map(hash, (0, 1, 2, 3))
[0, 1, 2, 3]
>>> map(hash, ("namea", "nameb", "namec", "named"))
[-1658398457, -1658398460, -1658398459, -1658398462]
散列冲突(hash collision)

  Python字典是使用哈希表实现的,究其本质也是一个数组,其索引是通过哈希函数获得的.散列函数的目标是在数组中均匀分布键,良好的散列函数会最小化小化了冲突的可能,如果两个对象相等,那么它们的散列值一定相等.反之不一定成立,这时冲突就发生了,散列值相等,但是两个对象并不相等.Cpython中使用开放定址法(open addressing)来解决冲突.在构造字典的过程中,当键值(可hash的对象)连续时,hash函数可以很好的工作,但是不连续时会增加碰撞的可能.

在这里插入图片描述

开放地址(open addressing)

  开放寻址意思是,当冲突发生的时候,即上图中’z’的情况,索引3已经在数组中被使用,就寻找未被占用的索引,将键值’z’对应的value插入数组中对应的位置.

在这里插入图片描述

字典常见操作的时间复杂度
操作平均复杂度最坏复杂度
0获取元素O(1)O(n)
1修改元素O(1)O(n)
2删除元素O(1)O(n)
3复制O(n)O(n)
4遍历O(n)O(n)
注意!!!

  在复制和遍历字典的操作中,最坏情况是字典曾达到的最大元素数目,不是字典当前的元素数目.意思就是如果一个字典原来很大,后来删除了很多元素,遍历仍要花费很长时间.因此如果要频繁删除字典中的元素,同时字典较大,那么最好创建一个新的字典.

字典的缺点和替代方案
  • 缺点:保存元素的顺序和键的添加顺序大多数情况下是不一致的,(连续的整数键值除外,因为连续的整数has后的索引值仍是连续的整数.
  • 替代方案:Python的标准库collections,提供了一个OrderedDict有序的字典.
from collections import OrderedDict
OrderedDict((str(num):num) for num in range(5))

OrderedDict()

set集合

  当元素的顺序不重要的时候,集合是一个不错的数据结构.Python提供了两种集合类型:
  • set():可变,无序,有限的集合,元素唯一
  • frozenset():不可变,无序,元素唯一,可哈希
    • forzenset()可作为字典的键值,也可作为set()或forzenset()中的一个元素
    • set()为可变对象,所以set()不能作为set()和forzenset()中的元素,不然会引发TypeError
  • 集合推导:{num for num in range(10)}
实现细节

  在CPython中,集合其实就是带空值的字典,键值为集合的元素.因此集合元素也是可哈希的,所以集合中的元素也依赖于类似散列表的结构,平均时间复杂度为O(1),最坏的情况O(n).

超越基础集合的类型-collections

  • namedtuple() : 创建元组子类的工厂函数
  • deque : 双端队列,类似列表,是栈和队列的一般化,可以从两端添加/取出元素
  • ChainMap : 类似字典的类
  • Counter : 字典子类,对哈希对象进行计数
  • OrderedDict : 字典子类,创建有序的字典
  • derfaultdict : 字典子类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值