Эх сурвалжийг харах

更新了第7天的文档

jackfrued 6 жил өмнө
parent
commit
4fa36c9c54

+ 277 - 249
Day01-15/07.字符串和常用数据结构.md

@@ -4,183 +4,226 @@
 
 第二次世界大战促使了现代电子计算机的诞生,最初的目的用计算机来快速的完成导弹弹道的计算,因此在计算机刚刚诞生的那个年代,计算机处理的信息基本上都是数值型的信息,而世界上的第一台电子计算机ENIAC每秒钟能够完成约5000次浮点运算。随着时间的推移,虽然数值运算仍然是计算机日常工作中最为重要的事情之一,但是今天的计算机更多的时间需要处理的数据可能都是以文本的方式存在的,如果我们希望通过Python程序操作本这些文本信息,就必须要先了解字符串类型以及与它相关的知识。
 
-所谓**字符串**,就是由零个或多个字符组成的有限序列,一般记为![$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](./res/formula_5.png)。
+所谓**字符串**,就是由零个或多个字符组成的有限序列,一般记为![$${\displaystyle s=a_{1}a_{2}\dots a_{n}(0\leq n \leq \infty)}$$](./res/formula_5.png)。在Python程序中,如果我们把单个或多个字符用单引号或者双引号包围起来,就可以表示一个字符串。
 
-我们可以通过下面的代码来了解字符串的使用。
+```Python
+s1 = 'hello, world!'
+s2 = "hello, world!"
+# 以三个双引号或单引号开头的字符串可以折行
+s3 = """
+hello, 
+world!
+"""
+print(s1, s2, s3, end='')
+```
+
+可以在字符串中使用`\`(反斜杠)来表示转义,也就是说`\`后面的字符不再是它原来的意义,例如:`\n`不是代表反斜杠和字符n,而是表示换行;而`\t`也不是代表反斜杠和字符t,而是表示制表符。所以如果想在字符串中表示`'`要写成`\'`,同理想表示`\`要写成`\\`。可以运行下面的代码看看会输出什么。
 
 ```Python
-def main():
-    str1 = 'hello, world!'
-    # 通过len函数计算字符串的长度
-    print(len(str1))  # 13
-    # 获得字符串首字母大写的拷贝
-    print(str1.capitalize())  # Hello, world!
-    # 获得字符串变大写后的拷贝
-    print(str1.upper())  # HELLO, WORLD!
-    # 从字符串中查找子串所在位置
-    print(str1.find('or'))  # 8
-    print(str1.find('shit'))  # -1
-    # 与find类似但找不到子串时会引发异常
-    # print(str1.index('or'))
-    # print(str1.index('shit'))
-    # 检查字符串是否以指定的字符串开头
-    print(str1.startswith('He'))  # False
-    print(str1.startswith('hel'))  # True
-    # 检查字符串是否以指定的字符串结尾
-    print(str1.endswith('!'))  # True
-    # 将字符串以指定的宽度居中并在两侧填充指定的字符
-    print(str1.center(50, '*'))
-    # 将字符串以指定的宽度靠右放置左侧填充指定的字符
-    print(str1.rjust(50, ' '))
-    str2 = 'abc123456'
-    # 从字符串中取出指定位置的字符(下标运算)
-    print(str2[2])  # c
-    # 字符串切片(从指定的开始索引到指定的结束索引)
-    print(str2[2:5])  # c12
-    print(str2[2:])  # c123456
-    print(str2[2::2])  # c246
-    print(str2[::2])  # ac246
-    print(str2[::-1])  # 654321cba
-    print(str2[-3:-1])  # 45
-    # 检查字符串是否由数字构成
-    print(str2.isdigit())  # False
-    # 检查字符串是否以字母构成
-    print(str2.isalpha())  # False
-    # 检查字符串是否以数字和字母构成
-    print(str2.isalnum())  # True
-    str3 = '  jackfrued@126.com '
-    print(str3)
-    # 获得字符串修剪左右两侧空格的拷贝
-    print(str3.strip())
+s1 = '\'hello, world!\''
+s2 = '\n\\hello, world!\\\n'
+print(s1, s2, end='')
+```
 
+在`\`后面还可以跟一个八进制或者十六进制数来表示字符,例如`\141`和`\x61`都代表小写字母`a`,前者是八进制的表示法,后者是十六进制的表示法。也可以在`\`后面跟Unicode字符编码来表示字符,例如`\u9a86\u660a`代表的是中文“骆昊”。运行下面的代码,看看输出了什么。
 
-if __name__ == '__main__':
-    main()
+```Python
+s1 = '\141\142\143\x61\x62\x63'
+s2 = '\u9a86\u660a'
+print(s1, s2)
 ```
 
-除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典
+如果不希望字符串中的`\`表示转义,我们可以通过在字符串的最前面加上字母`r`来加以说明,再看看下面的代码又会输出什么。
 
-### 使用列表
+```Python
+s1 = r'\'hello, world!\''
+s2 = r'\n\\hello, world!\\\n'
+print(s1, s2, end='')
+```
 
-下面的代码演示了如何定义列表、使用下标访问列表元素以及添加和删除元素的操作。
+Python为字符串类型提供了非常丰富的运算符,我们可以使用`+`运算符来实现字符串的拼接,可以使用`*`运算符来重复一个字符串的内容,可以使用`in`和`not in`来判断一个字符串是否包含另外一个字符串,我们也可以用`[]`和`[:]`运算符从字符串取出某个字符或某些字符,代码如下所示。
 
 ```Python
-def main():
-    list1 = [1, 3, 5, 7, 100]
-    print(list1)
-    list2 = ['hello'] * 5
-    print(list2)
-    # 计算列表长度(元素个数)
-    print(len(list1))
-    # 下标(索引)运算
-    print(list1[0])
-    print(list1[4])
-    # print(list1[5])  # IndexError: list index out of range
-    print(list1[-1])
-    print(list1[-3])
-    list1[2] = 300
-    print(list1)
-    # 添加元素
-    list1.append(200)
-    list1.insert(1, 400)
-    list1 += [1000, 2000]
-    print(list1)
-    print(len(list1))
-    # 删除元素
-    list1.remove(3)
-    if 1234 in list1:
-        list1.remove(1234)
-    del list1[0]
-    print(list1)
-    # 清空列表元素
-    list1.clear()
-    print(list1)
+s1 = 'hello ' * 3
+print(s1) # hello hello hello 
+s2 = 'world'
+s1 += s2
+print(s1) # hello hello hello world
+print('ll' in s1) # True
+print('good' in s1) # False
+str2 = 'abc123456'
+# 从字符串中取出指定位置的字符(下标运算)
+print(str2[2]) # c
+# 字符串切片(从指定的开始索引到指定的结束索引)
+print(str2[2:5]) # c12
+print(str2[2:]) # c123456
+print(str2[2::2]) # c246
+print(str2[::2]) # ac246
+print(str2[::-1]) # 654321cba
+print(str2[-3:-1]) # 45
+```
 
+在Python中,我们还可以通过一系列的方法来完成对字符串的处理,代码如下所示。
 
-if __name__ == '__main__':
-    main()
+```Python
+str1 = 'hello, world!'
+# 通过内置函数len计算字符串的长度
+print(len(str1)) # 13
+# 获得字符串首字母大写的拷贝
+print(str1.capitalize()) # Hello, world!
+# 获得字符串每个单词首字母大写的拷贝
+print(str1.title()) # Hello, World!
+# 获得字符串变大写后的拷贝
+print(str1.upper()) # HELLO, WORLD!
+# 从字符串中查找子串所在位置
+print(str1.find('or')) # 8
+print(str1.find('shit')) # -1
+# 与find类似但找不到子串时会引发异常
+# print(str1.index('or'))
+# print(str1.index('shit'))
+# 检查字符串是否以指定的字符串开头
+print(str1.startswith('He')) # False
+print(str1.startswith('hel')) # True
+# 检查字符串是否以指定的字符串结尾
+print(str1.endswith('!')) # True
+# 将字符串以指定的宽度居中并在两侧填充指定的字符
+print(str1.center(50, '*'))
+# 将字符串以指定的宽度靠右放置左侧填充指定的字符
+print(str1.rjust(50, ' '))
+str2 = 'abc123456'
+# 检查字符串是否由数字构成
+print(str2.isdigit())  # False
+# 检查字符串是否以字母构成
+print(str2.isalpha())  # False
+# 检查字符串是否以数字和字母构成
+print(str2.isalnum())  # True
+str3 = '  jackfrued@126.com '
+print(str3)
+# 获得字符串修剪左右两侧空格之后的拷贝
+print(str3.strip())
 ```
 
-和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。
+我们之前讲过,可以用下面的方式来格式化输出字符串
 
 ```Python
-def main():
-    fruits = ['grape', 'apple', 'strawberry', 'waxberry']
-    fruits += ['pitaya', 'pear', 'mango']
-    # 循环遍历列表元素
-    for fruit in fruits:
-        print(fruit.title(), end=' ')
-    print()
-    # 列表切片
-    fruits2 = fruits[1:4]
-    print(fruits2)
-    # fruit3 = fruits  # 没有复制列表只创建了新的引用
-    # 可以通过完整切片操作来复制列表
-    fruits3 = fruits[:]
-    print(fruits3)
-    fruits4 = fruits[-3:-1]
-    print(fruits4)
-    # 可以通过反向切片操作来获得倒转后的列表的拷贝
-    fruits5 = fruits[::-1]
-    print(fruits5)
+a, b = 5, 10
+print('%d * %d = %d' % (a, b, a * b))
+```
 
+当然,我们也可以用字符串提供的方法来完成字符串的格式,代码如下所示。
 
-if __name__ == '__main__':
-    main()
+```Python
+a, b = 5, 10
+print('{0} * {1} = {2}'.format(a, b, a * b))
 ```
 
-下面的代码实现了对列表的排序操作。
+Python 3.6以后,格式化字符串还有更为简洁的书写方式,就是在字符串前加上字母`f`,我们可以使用下面的语法糖来简化上面的代码。
 
 ```Python
-def main():
-    list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry']
-    list2 = sorted(list1)
-    # sorted函数返回列表排序后的拷贝不会修改传入的列表
-    # 函数的设计就应该像sorted函数一样尽可能不产生副作用
-    list3 = sorted(list1, reverse=True)
-    # 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序
-    list4 = sorted(list1, key=len)
-    print(list1)
-    print(list2)
-    print(list3)
-    print(list4)
-    # 给列表对象发出排序消息直接在列表对象上进行排序
-    list1.sort(reverse=True)
-    print(list1)
+a, b = 5, 10
+print(f'{a} * {b} = {a * b}')
+```
 
+除了字符串,Python还内置了多种类型的数据结构,如果要在程序中保存和操作数据,绝大多数时候可以利用现有的数据结构来实现,最常用的包括列表、元组、集合和字典。
 
-if __name__ == '__main__':
-    main()
+### 使用列表
+
+下面的代码演示了如何定义列表、使用下标访问列表元素以及添加和删除元素的操作。
+
+```Python
+list1 = [1, 3, 5, 7, 100]
+print(list1)
+list2 = ['hello'] * 5
+print(list2)
+# 计算列表长度(元素个数)
+print(len(list1))
+# 下标(索引)运算
+print(list1[0])
+print(list1[4])
+# print(list1[5])  # IndexError: list index out of range
+print(list1[-1])
+print(list1[-3])
+list1[2] = 300
+print(list1)
+# 添加元素
+list1.append(200)
+list1.insert(1, 400)
+list1 += [1000, 2000]
+print(list1)
+print(len(list1))
+# 删除元素
+list1.remove(3)
+if 1234 in list1:
+    list1.remove(1234)
+del list1[0]
+print(list1)
+# 清空列表元素
+list1.clear()
+print(list1)
 ```
 
-我们还可以使用列表的生成式语法来创建列表,代码如下所示。
+和字符串一样,列表也可以做切片操作,通过切片操作我们可以实现对列表的复制或者将列表中的一部分取出来创建出新的列表,代码如下所示。
 
 ```Python
-import sys
+fruits = ['grape', 'apple', 'strawberry', 'waxberry']
+fruits += ['pitaya', 'pear', 'mango']
+# 循环遍历列表元素
+for fruit in fruits:
+    print(fruit.title(), end=' ')
+print()
+# 列表切片
+fruits2 = fruits[1:4]
+print(fruits2)
+# fruit3 = fruits  # 没有复制列表只创建了新的引用
+# 可以通过完整切片操作来复制列表
+fruits3 = fruits[:]
+print(fruits3)
+fruits4 = fruits[-3:-1]
+print(fruits4)
+# 可以通过反向切片操作来获得倒转后的列表的拷贝
+fruits5 = fruits[::-1]
+print(fruits5)
+```
 
+下面的代码实现了对列表的排序操作。
 
-def main():
-    f = [x for x in range(1, 10)]
-    print(f)
-    f = [x + y for x in 'ABCDE' for y in '1234567']
-    print(f)
-    # 用列表的生成表达式语法创建列表容器
-    # 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
-    f = [x ** 2 for x in range(1, 1000)]
-    print(sys.getsizeof(f))  # 查看对象占用内存的字节数
-    print(f)
-    # 请注意下面的代码创建的不是一个列表而是一个生成器对象
-    # 通过生成器可以获取到数据但它不占用额外的空间存储数据
-    # 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间)
-    f = (x ** 2 for x in range(1, 1000))
-    print(sys.getsizeof(f))  # 相比生成式生成器不占用存储数据的空间
-    print(f)
-    for val in f:
-        print(val)
+```Python
+list1 = ['orange', 'apple', 'zoo', 'internationalization', 'blueberry']
+list2 = sorted(list1)
+# sorted函数返回列表排序后的拷贝不会修改传入的列表
+# 函数的设计就应该像sorted函数一样尽可能不产生副作用
+list3 = sorted(list1, reverse=True)
+# 通过key关键字参数指定根据字符串长度进行排序而不是默认的字母表顺序
+list4 = sorted(list1, key=len)
+print(list1)
+print(list2)
+print(list3)
+print(list4)
+# 给列表对象发出排序消息直接在列表对象上进行排序
+list1.sort(reverse=True)
+print(list1)
+```
 
+我们还可以使用列表的生成式语法来创建列表,代码如下所示。
 
-if __name__ == '__main__':
-    main()
+```Python
+f = [x for x in range(1, 10)]
+print(f)
+f = [x + y for x in 'ABCDE' for y in '1234567']
+print(f)
+# 用列表的生成表达式语法创建列表容器
+# 用这种语法创建列表之后元素已经准备就绪所以需要耗费较多的内存空间
+f = [x ** 2 for x in range(1, 1000)]
+print(sys.getsizeof(f))  # 查看对象占用内存的字节数
+print(f)
+# 请注意下面的代码创建的不是一个列表而是一个生成器对象
+# 通过生成器可以获取到数据但它不占用额外的空间存储数据
+# 每次需要数据的时候就通过内部的运算得到数据(需要花费额外的时间)
+f = (x ** 2 for x in range(1, 1000))
+print(sys.getsizeof(f))  # 相比生成式生成器不占用存储数据的空间
+print(f)
+for val in f:
+    print(val)
 ```
 
 除了上面提到的生成器语法,Python中还有另外一种定义生成器的方式,就是通过`yield`关键字将一个普通函数改造成生成器函数。下面的代码演示了如何实现一个生成[斐波拉切数列](https://zh.wikipedia.org/wiki/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97)的生成器。所谓斐波拉切数列可以通过下面[递归](https://zh.wikipedia.org/wiki/%E9%80%92%E5%BD%92)的方法来进行定义:
@@ -215,36 +258,31 @@ if __name__ == '__main__':
 Python 的元组与列表类似,不同之处在于元组的元素不能修改,在前面的代码中我们已经不止一次使用过元组了。顾名思义,我们把多个元素组合到一起就形成了一个元组,所以它和列表一样可以保存多条数据。下面的代码演示了如何定义和使用元组。
 
 ```Python
-def main():
-    # 定义元组
-    t = ('骆昊', 38, True, '四川成都')
-    print(t)
-    # 获取元组中的元素
-    print(t[0])
-    print(t[3])
-    # 遍历元组中的值
-    for member in t:
-        print(member)
-    # 重新给元组赋值
-    # t[0] = '王大锤'  # TypeError
-    # 变量t重新引用了新的元组原来的元组将被垃圾回收
-    t = ('王大锤', 20, True, '云南昆明')
-    print(t)
-    # 将元组转换成列表
-    person = list(t)
-    print(person)
-    # 列表是可以修改它的元素的
-    person[0] = '李小龙'
-    person[1] = 25
-    print(person)
-    # 将列表转换成元组
-    fruits_list = ['apple', 'banana', 'orange']
-    fruits_tuple = tuple(fruits_list)
-    print(fruits_tuple)
-
-
-if __name__ == '__main__':
-    main()
+# 定义元组
+t = ('骆昊', 38, True, '四川成都')
+print(t)
+# 获取元组中的元素
+print(t[0])
+print(t[3])
+# 遍历元组中的值
+for member in t:
+    print(member)
+# 重新给元组赋值
+# t[0] = '王大锤'  # TypeError
+# 变量t重新引用了新的元组原来的元组将被垃圾回收
+t = ('王大锤', 20, True, '云南昆明')
+print(t)
+# 将元组转换成列表
+person = list(t)
+print(person)
+# 列表是可以修改它的元素的
+person[0] = '李小龙'
+person[1] = 25
+print(person)
+# 将列表转换成元组
+fruits_list = ['apple', 'banana', 'orange']
+fruits_tuple = tuple(fruits_list)
+print(fruits_tuple)
 ```
 
 这里有一个非常值得探讨的问题,我们已经有了列表这种数据结构,为什么还需要元组这样的类型呢?
@@ -261,52 +299,47 @@ Python中的集合跟数学上的集合是一致的,不允许有重复元素
 ![](./res/python-set.png)
 
 ```Python
-def main():
-    set1 = {1, 2, 3, 3, 3, 2}
-    print(set1)
-    print('Length =', len(set1))
-    set2 = set(range(1, 10))
-    print(set2)
-    set1.add(4)
-    set1.add(5)
-    set2.update([11, 12])
-    print(set1)
-    print(set2)
-    set2.discard(5)
-    # remove的元素如果不存在会引发KeyError
-    if 4 in set2:
-        set2.remove(4)
-    print(set2)
-    # 遍历集合容器
-    for elem in set2:
-        print(elem ** 2, end=' ')
-    print()
-    # 将元组转换成集合
-    set3 = set((1, 2, 3, 3, 2, 1))
-    print(set3.pop())
-    print(set3)
-    # 集合的交集、并集、差集、对称差运算
-    print(set1 & set2)
-    # print(set1.intersection(set2))
-    print(set1 | set2)
-    # print(set1.union(set2))
-    print(set1 - set2)
-    # print(set1.difference(set2))
-    print(set1 ^ set2)
-    # print(set1.symmetric_difference(set2))
-    # 判断子集和超集
-    print(set2 <= set1)
-    # print(set2.issubset(set1))
-    print(set3 <= set1)
-    # print(set3.issubset(set1))
-    print(set1 >= set2)
-    # print(set1.issuperset(set2))
-    print(set1 >= set3)
-    # print(set1.issuperset(set3))
-
-
-if __name__ == '__main__':
-    main()
+set1 = {1, 2, 3, 3, 3, 2}
+print(set1)
+print('Length =', len(set1))
+set2 = set(range(1, 10))
+print(set2)
+set1.add(4)
+set1.add(5)
+set2.update([11, 12])
+print(set1)
+print(set2)
+set2.discard(5)
+# remove的元素如果不存在会引发KeyError
+if 4 in set2:
+    set2.remove(4)
+print(set2)
+# 遍历集合容器
+for elem in set2:
+    print(elem ** 2, end=' ')
+print()
+# 将元组转换成集合
+set3 = set((1, 2, 3, 3, 2, 1))
+print(set3.pop())
+print(set3)
+# 集合的交集、并集、差集、对称差运算
+print(set1 & set2)
+# print(set1.intersection(set2))
+print(set1 | set2)
+# print(set1.union(set2))
+print(set1 - set2)
+# print(set1.difference(set2))
+print(set1 ^ set2)
+# print(set1.symmetric_difference(set2))
+# 判断子集和超集
+print(set2 <= set1)
+# print(set2.issubset(set1))
+print(set3 <= set1)
+# print(set3.issubset(set1))
+print(set1 >= set2)
+# print(set1.issuperset(set2))
+print(set1 >= set3)
+# print(set1.issuperset(set3))
 ```
 
 > **说明:** Python中允许通过一些特殊的方法来为某种类型或数据结构自定义运算符(后面的章节中会讲到),上面的代码中我们对集合进行运算的时候可以调用集合对象的方法,也可以直接使用对应的运算符,例如`&`运算符跟intersection方法的作用就是一样的,但是使用运算符让代码更加直观。
@@ -316,35 +349,30 @@ if __name__ == '__main__':
 字典是另一种可变容器模型,类似于我们生活中使用的字典,它可以存储任意类型对象,与列表、集合不同的是,字典的每个元素都是由一个键和一个值组成的“键值对”,键和值通过冒号分开。下面的代码演示了如何定义和使用字典。
 
 ```Python
-def main():
-    scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82}
-    # 通过键可以获取字典中对应的值
-    print(scores['骆昊'])
-    print(scores['狄仁杰'])
-    # 对字典进行遍历(遍历的其实是键再通过键取对应的值)
-    for elem in scores:
-        print('%s\t--->\t%d' % (elem, scores[elem]))
-    # 更新字典中的元素
-    scores['白元芳'] = 65
-    scores['诸葛王朗'] = 71
-    scores.update(冷面=67, 方启鹤=85)
-    print(scores)
-    if '武则天' in scores:
-        print(scores['武则天'])
-    print(scores.get('武则天'))
-    # get方法也是通过键获取对应的值但是可以设置默认值
-    print(scores.get('武则天', 60))
-    # 删除字典中的元素
-    print(scores.popitem())
-    print(scores.popitem())
-    print(scores.pop('骆昊', 100))
-    # 清空字典
-    scores.clear()
-    print(scores)
-
-
-if __name__ == '__main__':
-    main()
+scores = {'骆昊': 95, '白元芳': 78, '狄仁杰': 82}
+# 通过键可以获取字典中对应的值
+print(scores['骆昊'])
+print(scores['狄仁杰'])
+# 对字典进行遍历(遍历的其实是键再通过键取对应的值)
+for elem in scores:
+    print('%s\t--->\t%d' % (elem, scores[elem]))
+# 更新字典中的元素
+scores['白元芳'] = 65
+scores['诸葛王朗'] = 71
+scores.update(冷面=67, 方启鹤=85)
+print(scores)
+if '武则天' in scores:
+    print(scores['武则天'])
+print(scores.get('武则天'))
+# get方法也是通过键获取对应的值但是可以设置默认值
+print(scores.get('武则天', 60))
+# 删除字典中的元素
+print(scores.popitem())
+print(scores.popitem())
+print(scores.pop('骆昊', 100))
+# 清空字典
+scores.clear()
+print(scores)
 ```
 
 ### 练习

+ 5 - 0
更新日志.md

@@ -1,5 +1,10 @@
 ## 更新日志
 
+### 2019年9月23日
+
+1. 计划在国庆长假结束前,完成第91天到第100天内容的更新,包括最新的Python面试题集。
+2. 修改了第7天《字符串和常用数据结构》一文的内容。
+
 ### 2019年9月15日
 
 1. 将微信打赏所得收入通过腾讯公益捐赠给国家级贫困大学生。