欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

chapter_2

程序员文章站 2022-03-05 12:21:29
...

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函数可以很好的工作,但是不连续时会增加碰撞的可能.

chapter_2

开放地址(open addressing)

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

chapter_2

字典常见操作的时间复杂度

操作 平均复杂度 最坏复杂度
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 : 字典子类