Python学习笔记

学习自廖雪峰的官方网站,加入了一些自己的理解,或许会有错误。

编码和字符串问题

Asicc

将大小写英文字母、数字和一些符号进行编码得到Asicc码表。

由于1Byte为8bit,即1字节等于8位,而8bit所能表示的最大无符号数为 (11111111)2 = (255) 10,所以Asicc的编码大多用 1Byte 即可表示。
而汉字多达几万个,Asicc码无法表示这么多汉字,所以(猜测)中国制定了 GB2312编码,使用两个字节对中文进行编码。
每个国家语言不同,制定的编码规则也不相同,所以在某些情况下会出现编码冲突,导致乱码的出现。于是为了解决这个问题,Unicode编码应运而生。

Unicode

Unicode码统一了所有语言的编码,通常情况下用两个字节表示一个字符,特殊情况下会增加字节的使用来表示一个字符。而原先由一个字节表示的 Asicc码到了Unicode码中更改为两个字节表示,其中高位添0。如’A’的 Asicc码为 (65)10 = (01000001)2,到了 Unicode码则表示为 (65)10 = (00000000 01000001)2

由于Asicc码少一个字节,在大量英文文本的传输或保存中若使用 Unicode 码进行编码,则会使存储空间比用Asicc码多出一倍,大大浪费时间和空间。为了解决这个问题,又出现了”可变长编码”的UTF-8编码。

UTF-8

UTF-8编码将Unicode码编成1-6个字节,常用英文字母还是一个字节,而汉字在UTF-8编码中通常是3个字节。在大量英文文本的传输或保存中可大大节约空间,且UTF-8编码对只支持Acicc编码的软件具有很好的兼容性。

字符串和编码的转换

计算机内存里面,使用的是Unicode编码,当需要进行保存或者传输的时候需要转换成UTF-8编码。

Python提供了两个与编码相关的函数,ord()与chr()。前者可将对应字符的编码以十进制形式给出,如字符ord(‘A’)执行结果是65;后者可将对应十进制数转换为相应编码所对应的字符,如chr(65)执行结果是’A’。

Python字符串类型为str,在内存中以Unicode编码,所以在进行传输和保存的时候需要将其转换成字节形式。Python提供了encode()方法将对应字符转换成需要的字节编码形式。如’ABC’.encode(‘ascii’)即将str类型的’ABC’转换成ascii码的字节形式,执行结果即为b’ABC’,b表示bytes。纯英文的字符串可用Ascii将其编码为bytes,也可以用UTF-8将其
编码为bytes,但由于中文无法用Ascii码表示,所以中文不能用Ascii对其进行编码。

相反,若在网络或者磁盘上读取字节流,这个时候的数据就是bytes,如何将其转换成str类型?Python也提供了decode()方法将bytes类型的数据转换为str类型。如b’ABC’.decode(),执行结果为’ABC’。

在对字符串进行操作时,经常会对bytes和str进行转换(爬虫…?),所以一般情况下均使用UTF-8进行转换以避免麻烦的产生。

练习

(试一试…)
小明的成绩从去年的72分提升到了今年的85分,请计算小明成绩提升的百分点,并用字符串格式化显示出’xx.x%’,只保留小数点后1位:

1
2
3
4
5
6
7
# 1
print('{:.1%}'.format((85 - 72) / 72))
# 2
print('{:.1f}'.format((85 - 72) / 72 * 100))
# 3
# '%%'显示%号
print('%.1f%%' % ((85 - 72) / 72 * 100))

列表、元组、字典和集合

简单的例子:

1
2
3
4
5
6
7
8
9
10
# 列表
lists = [1, '2', 'ASD', 4]
# 元组
tuples = (1, 'A', '#')
# 字典
dicts = {'1': 123, '2': 234}
# 集合
sets = set([1, 2, 3, 'a'])

print(lists, tuples, dicts, sets, sep='\n')

当然列表、元组、字典和集合在一定条件下可自身嵌套或相互嵌套。

列表

类似于数组,可用下标即索引(由0开始)来进行访问,在Python也可进行”逆向”访问,即lists列表的最后一个元素可以用lists[-1]进行访问,倒数第二个,倒数第三个以此类推。

lists是一个可变的有序表(可变且有序,有序指定义lists时的元素顺序)。

添加元素的方法有append(‘element’)方法,如lists.append(‘A’),即可向列表lists中添加元素到末尾。若想插入到指定位置,则可使用insert(‘postion’ , ‘elem’)方法,如lists.insert(0 , ‘C’),即可向索引为0的位置插入’C’这个元素。

若想删除末尾元素,直接调用pop()方法即可,如lists.pop(),直接将末尾元素删除,若想删除指定位置的元素,使用pop(pos)方法即可,如lists.pop(0),将删除索引为0位置的元素。

若想更改某个元素,直接找到索引赋值即可,如lists[0] = ‘Q’。

元组

与列表相似,但不同的地方在于,列表初始化后可以任意修改,但元组初始化后不能修改(有序但不可变)。这在一定程度上提高了元组相对于列表的安全性。

元组也可通过索引进行访问,但由于不可变,所以元组本身并没有添加和删除元素的方法。

注意,定义一个空的元组使用tuples = ()语句,但定义拥有一个元素的元组则需要使用tuples = (1,)语句而非tuples = (1)。这是因为若使用后者,则会与小括号()产生歧义,为了避免此种情况的发生,则在只有一个元素的元组的情况下,需要在其后面加上一个逗号消除歧义。

对于以下这个例子:

1
2
3
4
5
t = ('a', 'b', ['A', 'B'])
print(t)
t[2][0] = 'X'
t[2][1] = 'Y'
print(t)

执行结果是:
(‘a’, ‘b’, [‘A’, ‘B’])
(‘a’, ‘b’, [‘X’, ‘Y’])

元组的定义中有提到元组相对于列表是不可变的,但在此处为何更改了值?我的理解则是列表[‘A’ , ‘B’]内的元素是可变的,与列表定义不冲突,但这个列表是作为元组的一个元组存在的,所以元组的第3个位置指向了这个列表,改变的是列表的值而并非更改了指向这个列表的指针(用C语言理解或许是这样?),所以可以更改,而指向这个列表的指针在定义这个元组的时候就已经确定,所以与元组不可变的定义并不冲突。

练习

请用索引取出下面list的指定元素:

1
2
3
4
5
6
L = [  
['Apple', 'Google', 'Microsoft'],
['Java', 'Python', 'Ruby', 'PHP'],
['Adam', 'Bart', 'Lisa']
]
打印Apple,Python,Lisa

其实可以用二维数组的方式来理解这个列表,L[0][0]即是Apple,代码如下:

1
2
3
4
5
6
7
8
9
10
L = [
['Apple', 'Google', 'Microsoft'],
['Java', 'Python', 'Ruby', 'PHP'],
['Adam', 'Bart', 'Lisa']
]

print(L[0][0], L[1][1], L[2][2], sep="\n")

# 若打印Apple的第二个字符p
print(L[0][0][1])

字典

与C++ STL容器中的map容器类似(或许实现方式也差不多?),指示了键值之间的关系。

若想判断某个键是否存在,则可使用in判断。如’1’ in dicts,执行结果是True(最开始的定义语句)。还可以使用get()方法,如dicts.get(‘1’ , -1),若1不存在则返回-1,后一项指示键不存在的时候用哪个数字作为返回值,不传入则返回None。

若想删除某个键,则可以使用pop(key)方法,对应的值也会删除,如dicts.pop(‘1’)。

若想添加某个键值之间的关系,也可以使用dicts[‘3’] = 789,这样的语句。

字典的键必须是不可变对象,如字符串、整数等。如果是可变的对象如列表就不能作为键,如果使用列表作为键会导致哈希算法的出错,导致整个字典的出错。

字典的内部存放顺序并不一定与定义的时候所采用的顺序相同。(无序)

集合

与C++ STL容器中的set容器类似,可以达到去重的效果。Python中set是一组键的集合,但并没有所对应的值。

与字典类似,集合的内部存放顺序也不一定与定义的时候所采用的顺序相同(无序)。

若想要往集合中添加元素,则可使用add(key)方法,如sets.add(9)。

若想要删除集合中的元素,则可使用remove(key)方法,如sets.remove(9)。

set集合也可以理解成数学上的集合,可以进行交集并集的运算,运算符采用&,和|。

如:

1
2
3
4
5
s1 = set([1, 2, 3])
# 或
# s1 = {1 , 2 , 3}
s2 = set([2, 3, 4])
print(s1 & s2, s1 | s2, sep="\n")

执行结果是:
{2, 3}
{1, 2, 3, 4}

集合与字典的原理一样,因此同样也不可放入可变对象。