diff --git a/docs/L0/Python/ch5_python_intro.md b/docs/L0/Python/ch5_python_intro.md new file mode 100644 index 000000000..98996828e --- /dev/null +++ b/docs/L0/Python/ch5_python_intro.md @@ -0,0 +1,1362 @@ +# Python基础速通教程 + +## 1 Python基础语法 + + +```python +#这是一个单行注释 +''' +这是一个多行注释 +这是一个多行注释 +''' +print("hello world")#从hello world开始认识Python +print(1+1) +print(1+2) +#print函数是Python中用于输出信息到控制台的内置函数。 +``` + + hello world + 2 + 3 + + +在python中,代码块不使用{}来表示,而是通过行与缩进来表示,同一个代码块有着相同的缩进。 + + +```python +if True: + print('hello world!') + print('hello python!') +else: + print('goodbye python!') + print('goodbye world!') +``` + + hello world! + hello python! + + +python标识符(变量名,函数名)命名要求: +* 标识符须要由字母,数字和下划线组成 +* 首字符必须是字母或者下划线_ +* 标识符对大小写敏感 +* 标识符不能与python关键字重名 + +以下代码可以查看python的关键词(保留字符) + + +```python +import keyword +print(keyword.kwlist) +``` + + ['False', 'None', 'True', 'and', 'as', 'assert', 'async', 'await', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield'] + + +## 2 基本数据类型 + +python基本数据类型: +* 数字Number + * int整数 + * float浮点数 + * complex复数 +* 布尔型bool +* 列表list +* 元组tuple +* 集合set +* 字典Dictionary + +可以修改的是:列表list,集合set,字典Dictionary + +不可以修改的是:数字Number,布尔型bool,元组tuple + + +```python +#python中变量赋值的方式 +a = 1 +b = 2 +print(a+b) +a,b = 1,2 #另一种单行赋值的方式 +print(1,2) + +#下面让我们来看看各种类型的数据长什么样 +t_int = 1 #整数 +t_float = 1.0 #浮点数 +t_complex = 1.2j #复数 +t_bool = True #布尔类型 +t_list = [1,1,3,3,5,5] #列表 +t_tuple = (1,1,3,3,5,5) #元组 +t_set = {1,3,5} #集合 +t_dict = {'day':18,'month':6,'year':2024} #字典 + +print(" t_int的类型是:",type(t_int),' t_int的值是: ',t_int) +print(" t_float的类型是:",type(t_float),' t_float的值是: ',t_float) +print(" t_complex的类型是:",type(t_complex),' t_complex的值是: ',t_complex) +print(" t_bool的类型是:",type(t_bool),' t_bool的值是: ',t_bool) +print(" t_list的类型是:",type(t_list),' t_list的值是: ',t_list) +print(" t_tuple的类型是:",type(t_tuple),' t_tuple的值是: ',t_tuple) +print(" t_set的类型是:",type(t_set),' t_set的值是: ',t_set) +print(" t_dict的类型是:",type(t_dict),' t_int的值是: ',t_dict) +``` + + 3 + 1 2 + t_int的类型是: t_int的值是: 1 + t_float的类型是: t_float的值是: 1.0 + t_complex的类型是: t_complex的值是: 1.2j + t_bool的类型是: t_bool的值是: True + t_list的类型是: t_list的值是: [1, 1, 3, 3, 5, 5] + t_tuple的类型是: t_tuple的值是: (1, 1, 3, 3, 5, 5) + t_set的类型是: t_set的值是: {1, 3, 5} + t_dict的类型是: t_int的值是: {'day': 18, 'month': 6, 'year': 2024} + + +## 3 运算符 +### 3.1 算数运算符 +| 运算符 | 描述 | +| --- | --- | +| `+` | 加 | +| `-` | 减 | +| `*` | 乘 | +| `/` | 除 | +| `//` | 整除,只保留整数 | +| `%` | 模运算或者取余数除法,只保留余数 | +| `**` | 幂运算 | + + +```python +print("1+1 =",1+1) +print("2-1 =",2-1) +print("2*4 =",2*4) +print("2*4 =",2*4) +print("8//3 =",8//3) +print("8%5 =",8%5) +print("2**3 =",2**3) +``` + + 1+1 = 2 + 2-1 = 1 + 2*4 = 8 + 2*4 = 8 + 8//3 = 2 + 8%5 = 3 + 2**3 = 8 + + +### 3.2 比较运算符 +| 运算符 | 描述 | +| --- | --- | +| `>` | 大于 | +| `<` | 小于 | +| `>=` | 大于等于 | +| `<=` | 小于等于 | +| `==` | 等于 | +| `!=` | 不等于 | + + +```python +a,b = 10,20 +print("a>b =",a>b) +print("a=b =",a>=b) +print("a<=b =",a<=b) +print("a==b =",a==b) +print("a!=b =",a!=b) +``` + + a>b = False + a=b = False + a<=b = True + a==b = False + a!=b = True + + +### 3.3 赋值运算符 +| 运算符 | 描述 | 等价表达 | +| --- | --- | --- | +| `=` | 基础赋值 | | +| `+=` | 加法赋值 | a+=b等价于 a=a+b | +| `-=` | 减法赋值 | a-=b等价于 a=a-b | +| `*=` | 小于等于 | a*=b等价于 a=a*b | +| `/=` | 等于 | a/=b等价于 a=a/b | +| `//=` | 等于 | a//=b等价于 a=a//b | +| `%=` | 等于 | a%=b等价于 a=a%b | +| `**=` | 等于 | a**=b等价于 a=a**b | + + +```python +a,b = 2,2 +a += 1 +b = b+1 +print("a+=1,a=",a) +print("b=b+1,b=",b) + +print("-"*20) +a,b = 2,2 +a -= 1 +b = b-1 +print("a-=1,a=",a) +print("b=b-1,b=",b) + +print("-"*20) +a,b = 2,2 +a *= 2 +b = b*2 +print("a*=2,a=",a) +print("b=b*2,b=",b) + +print("-"*20) +a,b = 2,2 +a /= 2 +b = b/2 +print("a/=2,a=",a) +print("b=b/2,b=",b) + +print("-"*20) +a,b = 2,2 +a //= 2 +b = b//2 +print("a//=2,a=",a) +print("b=b//2,b=",b) + +print("-"*20) +a,b = 2,2 +a %= 3 +b = b%3 +print("a%=3,a=",a) +print("b=b%3,b=",b) + +print("-"*20) +a,b = 2,2 +a %= 3 +b = b%3 +print("a%=3,a=",a) +print("b=b%3,b=",b) + +print("-"*20) +a,b = 2,2 +a **= 3 +b = b**3 +print("a**=3,a=",a) +print("b=b**3,b=",b) +``` + + a+=1,a= 3 + b=b+1,b= 3 + -------------------- + a-=1,a= 1 + b=b-1,b= 1 + -------------------- + a*=2,a= 4 + b=b*2,b= 4 + -------------------- + a/=2,a= 1.0 + b=b/2,b= 1.0 + -------------------- + a//=2,a= 1 + b=b//2,b= 1 + -------------------- + a%=3,a= 2 + b=b%3,b= 2 + -------------------- + a%=3,a= 2 + b=b%3,b= 2 + -------------------- + a**=3,a= 8 + b=b**3,b= 8 + + +### 3.4 逻辑运算符 +| 运算符 | 描述 | +| --- | --- | +| `and` | 逻辑与 | +| `or` | 逻辑或 | +| `not` | 逻辑非 | + + +```python +a,b = True,False +print("a and b =",a and b) +print("a or b =",a and b) +print("not b =",not b) +``` + + a and b = False + a or b = False + not b = True + + +## 4 列表 +列表是一个有索引的序列,索引从0开始。 + +列表式一个有序的可重复元素序列。列表是可变的,列表中的元素可以重复,可以是不同的类型。 + + +列表非常重要,他不仅是python开发中最常用的数据结构,同时也是python序列中最具代表性的一个。 当能够理解列表以后,剩下的其他序列学起来就没有那么困难。 + +### 4.1 列表的创建方法 + + +```python +alist = [1,2,3,4,5,6] #列表使用[]表示,每个元素之间用,隔开 +print('alist :',alist) +blist = list("hello world!") #列表也可以使用list()函数将其他可迭代的对象转换为列表,比如字符串。字符串的结构我们会在字符串小节里具体介绍 +print('blist :',blist) +``` + + alist : [1, 2, 3, 4, 5, 6] + blist : ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '!'] + + +### 4.2 访问元素的方法,索引与切片 + +python中的序列均有正向索引与反向索引,正向索引从0开始表示序列第一个元素,反向索引从-1开始表示 +以alist为例子,我们打印出alist中每个元素对于的正向索引与反向索引。 + + +```python +print('elements: ',' '.join([str(i) for i in alist])) +print('正向索引: ',' '.join([str(i) for i in range(0,len(alist))])) +print('反向索引: ',' '.join([str(i) for i in range(-len(alist),0)]))#这里的len函数是用来获取列表长度的,后面会具体介绍 +``` + + elements: 1 2 3 4 5 6 + 正向索引: 0 1 2 3 4 5 + 反向索引: -6 -5 -4 -3 -2 -1 + + + +```python +#取列表第一个元素的两种方式 +print('第一个元素: ',alist[0]) +print('第一个元素: ',alist[-6]) + +#取列表最后一个元素的两种方式 +print('最后一个元素: ',alist[-1]) +print('最后一个元素: ',alist[5]) + +#修改第二个元素 +print('alist[1]=0') +alist[1]=0 +print(alist) + +#删除第二个元素 +del alist[1] +print(alist) +``` + + 第一个元素: 1 + 第一个元素: 1 + 最后一个元素: 6 + 最后一个元素: 6 + alist[1]=0 + [1, 0, 3, 4, 5, 6] + [1, 3, 4, 5, 6] + + +列表支持切片操作,即截取列表中的一部分长度的操作。 + +具体语法为 list_name\[start_index:end_index:step\]。 + +start_index为起点索引,end_index为终点索引,step为步长。要注意截取的部分为\[start_index,end_index\),也就是不包括end_index。 + +其中step缺省时默认步长为1,start_index缺省时默认为第一个元素,end_index缺省时默认为最后一个元素。 + + +```python +alist = [1,2,3,4,5,6] +print(alist) + +print('获取前三个元素') +print('alist[0:3]: ',alist[0:3]) +print('alist[:3]: ',alist[:3]) + +print('获取第二个到第四个元素(索引1至3)') +print('alist[1:4]: ',alist[1:4]) + +print('获取最后三个元素') +print('alist[-3:]: ',alist[-3:]) + +print('获取所有索引为奇数的元素') +print('alist[1::2]: ',alist[1::2]) + +print('获取所有索引为偶数的元素') +print('alist[::2]: ',alist[::2]) +``` + + [1, 2, 3, 4, 5, 6] + 获取前三个元素 + alist[0:3]: [1, 2, 3] + alist[:3]: [1, 2, 3] + 获取第二个到第四个元素(索引1至3) + alist[1:4]: [2, 3, 4] + 获取最后三个元素 + alist[-3:]: [4, 5, 6] + 获取所有索引为奇数的元素 + alist[1::2]: [2, 4, 6] + 获取所有索引为偶数的元素 + alist[::2]: [1, 3, 5] + + +### 4.3 列表运算符 +| 运算符 | 描述 | +| --- | --- | +| `+` | 拼接两个列表 | +| `*` | 列表*整数,将列表重复 | +| `in` | 元素是否在列表内| + + +```python +alist = [1,2,3] +blist = [4,5,6] +print('alist+blist: ',alist+blist) +print('alist*3: ',alist*3) +print('3 in alist: ',3 in alist) +print('4 in alist: ',4 in alist) +``` + + alist+blist: [1, 2, 3, 4, 5, 6] + alist*3: [1, 2, 3, 1, 2, 3, 1, 2, 3] + 3 in alist: True + 4 in alist: False + + +### 4.4 列表函数与方法 +| 函数名 | 描述 | +| --- | --- | +| `len()` | 列表长度 | +| `max()` | 列表中的最大值| +| `min()` | 列表中的最小值| + +| 方法名 | 描述 |返回值| +| --- | --- | --- | +| `list.append(obj)` | 在列表末尾添加新元素 | 无,原地操作 | +| `list.count(obj)` | 统计一个元素在列表中出现的次数 | 返回该元素出现的次数 | +| `list.extend(seq)` | 将一个序列中的所有元素添加到列表末尾 | 无,原地操作 | +| `list.index(obj)` | 从列表中找出某个值第一个匹配项的索引位置 | 返回索引 | +| `list.insert(index, obj)` | 将元素插入列表中的指定位置 | 无,原地操作 | +| `list.pop([index=-1])` | 移除列表中的一个元素(默认最后一个元素),并且返回该元素的值 | 返回移除的元素值 | +| `list.remove(obj)` | 移除列表中某个值第一个匹配项 | 无,原地操作 | +| `list.reverse()` | 逆向排列列表中的所有元素 | 无,原地操作 | +| `list.sort(key=None,reverse=False)` | 对列表内元素进行排序,默认为升序 | 无,原地操作 | +| `list.clear()` | 清空列表 | 无,原地操作 | +| `list.copy()` | 复制列表 | 复制后的列表 | + + +```python +alist = [0,1,1,2,3,4,5,6,7,8] +print(len(alist)) +print(max(alist)) +print(min(alist)) +alist.sort(key=None,reverse=False) +alist +``` + + 10 + 8 + 0 + + + + + + [0, 1, 1, 2, 3, 4, 5, 6, 7, 8] + + + + +```python +alist.append(10)#.append不会返回任何内容,直接在原列表上操作 +print(alist) +``` + + [0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 10] + + + +```python +print('10出现的次数:',alist.count(10))#.count会返回该元素出现的次数平 +print('1出现的次数:',alist.count(1)) +``` + + 10出现的次数: 1 + 1出现的次数: 2 + + + +```python +alist.extend([11,12])#.extend不会返回任何内容,直接在原列表上操作 +print(alist) +``` + + [0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12] + + + +```python +print('1出现的首个索引为',alist.index(1))#.index会返回匹配到的首个元素的索引,即1,而非2 +print('2出现的首个索引为',alist.index(2)) +``` + + 1出现的首个索引为 1 + 2出现的首个索引为 3 + + + +```python +alist.insert(1,0)#在索引为1的位置插入一个0,没有返回值 +print(alist) +``` + + [0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12] + + + +```python +print("移除最后一个元素: ",alist.pop())#移除最后元素,没有返回值 +print(alist) +print("移除索引为6的元素: ",alist.pop(6))#移除索引为6的元素,没有返回值 +print(alist) +``` + + 移除最后一个元素: 12 + [0, 0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11] + 移除索引为6的元素: 4 + [0, 0, 1, 1, 2, 3, 5, 6, 7, 8, 10, 11] + + + +```python +alist.remove(11)#删除第一个等于11的元素,但没有返回值 +print(alist) +``` + + [0, 0, 1, 1, 2, 3, 5, 6, 7, 8, 10] + + + +```python +alist.reverse()#逆向排列列表中的所有元素 +print(alist) +``` + + [10, 8, 7, 6, 5, 3, 2, 1, 1, 0, 0] + + + +```python +alist.sort()#将列表中的所有元素按升序排列,没有返回值 +print(alist) +alist.sort(reverse=True)#将列表中的所有元素按降序排列,没有返回值 +print(alist) +``` + + [0, 0, 1, 1, 2, 3, 5, 6, 7, 8, 10] + [10, 8, 7, 6, 5, 3, 2, 1, 1, 0, 0] + + + +```python +#copy和clear我们放到一起讲 +#如何备份一个alist?直接=和使用copy的区别? +alist_copy = alist# +print('alist_copy: ',alist_copy) +alist_copy.clear()#清空alist_copy +print('alist_copy: ',alist_copy) +print('alist: ',alist) +``` + + alist_copy: [10, 8, 7, 6, 5, 3, 2, 1, 1, 0, 0] + alist_copy: [] + alist: [] + + +alist_copy = alist时并没有新建一个对象,而只是单纯赋值了alist的地址给alist_copy。这会导致我们对变量alist_copy的所有操作本质上都是在操作alist。而当我们使用.copy()方法进行赋值时,会复制alist为一个新的对象,此时对新对象的修改不会影响到alist。 + + +```python +alist = [10, 8, 7, 6, 5, 3, 2, 1, 1, 0, 0] +alist_copy = alist.copy() +print('alist_copy: ',alist_copy) +alist_copy.clear()#清空alist_copy +print('alist_copy: ',alist_copy) +print('alist: ',alist) +``` + + alist_copy: [10, 8, 7, 6, 5, 3, 2, 1, 1, 0, 0] + alist_copy: [] + alist: [10, 8, 7, 6, 5, 3, 2, 1, 1, 0, 0] + + + +```python +33.6/100 +``` + + + + + 0.336 + + + + +```python +49.8/150 +``` + + + + + 0.33199999999999996 + + + +## 5 元组 + +元组和列表很相似,但是元组创建后不可修改。 + +元组使用()来表示,创建元组和列表一样,只要在()内添加元素并用逗号隔开就行。 + + +```python +atuple = (1,2,3) +print(atuple[0]) +print(atuple[1:3])#元组的索引与切片与列表一致 +``` + + 1 + (2, 3) + + + +```python +#当尝试修改元组内部的元素时,会报错 +atuple[1]=2 +``` + + + --------------------------------------------------------------------------- + + TypeError Traceback (most recent call last) + + ~\AppData\Local\Temp\ipykernel_13796\2873906481.py in + 1 #当尝试修改元组内部的元素时,会报错 + ----> 2 atuple[1]=2 + + + TypeError: 'tuple' object does not support item assignment + + +### 5.1 元组的运算符 +元组的运算符与列表一模一样 +| 运算符 | 描述 | +| --- | --- | +| `+` | 拼接两个元组 | +| `*` | 列表*整数,将元组重复 | +| `in` | 元素是否在元组内| + + +```python +atuple = (1,2,3) +btuple = (4,5,6) +print('alist+blist: ',atuple+btuple) +print('alist*3: ',atuple*3) +print('3 in alist: ',3 in atuple) +print('4 in alist: ',4 in atuple) +``` + + alist+blist: (1, 2, 3, 4, 5, 6) + alist*3: (1, 2, 3, 1, 2, 3, 1, 2, 3) + 3 in alist: True + 4 in alist: False + + +### 5.2 元组的函数 +元组支持的函数也与列表一模一样 +| 函数名 | 描述 | +| --- | --- | +| `len()` | 列表长度 | +| `max()` | 列表中的最大值| +| `min()` | 列表中的最小值| + + +```python +atuple = (1,2,3,4) +print(len(atuple)) +print(max(atuple)) +print(min(atuple)) +``` + + 4 + 4 + 1 + + +## 6 集合 +集合是无序的不重复的元素序列,支持交集、并集、差集等常见的集合操作。 + +集合的创建使用{}表示,并用,隔开。注意如果需要创建一个空集合需要使用set()。 + + +```python +#新建一个集合 +aset = {1,2,3,4,5} +print('这是一个新集合: ',aset) +``` + + 这是一个新集合: {1, 2, 3, 4, 5} + + + +```python +#新建一个空集合 +aset = set() +print('这是一个空集合: ',aset) +``` + + 这是一个空集合: set() + + + +```python +#将其他序列转换成集合,注意观察转换前后的变化 +alist = [1,1,1,1,2,2,2,2,3,3,3] +print('alist: ',alist) +aset = set(alist) +print('aset: ',aset) +``` + + alist: [1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3] + aset: {1, 2, 3} + + +### 6.1 集合的常用方法 +| 方法名称 | 描述 | 返回值 | +| --- | --- | --- | +| add() | 为集合添加元素 | 无返回值,原地操作 | +|clear() |移除集合中的所有元素| 无返回值,原地操作 | +| copy() |拷贝一个集合 | 返回拷贝后新创建的集合 | +| difference() | 返回多个集合的差集 | 返回差集结果 | +| intersection() | 返回集合的交集 |返回交集结果 | +| union() | 返回两个集合的并集 | 返回并集结果 | +| symmetric_difference() | 返回两个集合中不重复的元素集合 | 返回两个集合中不重复的元素集合 | +| isdisjoint() | 判断两个集合是否包含相同的元素 | bool,如果没有返回 True,否则返回 False | +| issubset() | 判断指定集合是否为该方法参数集合的子集 | bool,如果没有返回 True,否则返回 False | +| issuperset() | 判断该方法的参数集合是否为指定集合的子集 | bool,如果没有返回 True,否则返回 False | +| update() | 合并两个集合,传入的参数必须是一个集合 | 无返回值,原地操作 | +| pop() | 随机移除元素 | 返回被移除的元素 | +| remove() | 移除指定元素 | 无返回值,原地操作 | +| discard() | 删除集合中指定的元素 | 无返回值,原地操作 | + + + +```python +aset = {1,2,3} +bset = {3,4,5} +``` + + +```python +print(aset) +aset.add(4) +print(aset) +``` + + {1, 2, 3} + {1, 2, 3, 4} + + + +```python +#计算差集 +print('aset: ',aset) +print('bset: ',bset) +print('aset.difference(bset) :',aset.difference(bset)) +``` + + aset: {1, 2, 3, 4} + bset: {3, 4, 5} + aset.difference(bset) : {1, 2} + + + +```python +#计算交集 +print('aset: ',aset) +print('bset: ',bset) +print('aset.intersection(bset) :',aset.intersection(bset)) +``` + + aset: {1, 2, 3, 4} + bset: {3, 4, 5} + aset.intersection(bset) : {3, 4} + + + +```python +#计算并集 +print('aset: ',aset) +print('bset: ',bset) +print('aset.union(bset) :',aset.union(bset)) +``` + + aset: {1, 2, 3, 4} + bset: {3, 4, 5} + aset.union(bset) : {1, 2, 3, 4, 5} + + + +```python +#计算两个集合中不重复的元素集合 +print('aset: ',aset) +print('bset: ',bset) +print('aset.symmetric_difference(bset) :',aset.symmetric_difference(bset)) +``` + + aset: {1, 2, 3, 4} + bset: {3, 4, 5} + aset.symmetric_difference(bset) : {1, 2, 5} + + + +```python +#判断两个集合是否包含相同的元素 +print('aset: ',aset) +print('bset: ',bset) +print('aset.isdisjoint(bset) :',aset.isdisjoint(bset)) +print('aset.isdisjoint({-1,0}) :',aset.isdisjoint({-1,0})) +``` + + aset: {1, 2, 3, 4} + bset: {3, 4, 5} + aset.isdisjoint(bset) : False + aset.isdisjoint({-1,0}) : True + + + +```python +#判判断指定集合是否为该方法参数集合的子集 +print('aset: ',aset) +print('bset: ',bset) +print('aset.issubset(bset) :',aset.issubset(bset)) +print('aset.issubset(aset.union(bset)) :',aset.issubset(aset.union(bset))) +``` + + aset: {1, 2, 3, 4} + bset: {3, 4, 5} + aset.issubset(bset) : False + aset.issubset(aset.union(bset)) : True + + + +```python +#判判断指定集合是否为该方法参数集合的超集 +print('aset: ',aset) +print('bset: ',bset) +print('aset.issuperset(bset) :',aset.issuperset(bset)) +print('aset.issuperset(aset.union(bset)) :',aset.issuperset(aset.union(bset))) +``` + + aset: {1, 2, 3, 4} + bset: {3, 4, 5} + aset.issuperset(bset) : False + aset.issuperset(aset.union(bset)) : False + + + +```python +#合并两个集合 +print('aset: ',aset) +print('bset: ',bset) +aset.update(bset) +print('aset.update(bset)后的aset :',aset) +``` + + aset: {1, 2, 3, 4} + bset: {3, 4, 5} + aset.update(bset)后的aset : {1, 2, 3, 4, 5} + + + +```python +#使用pop随机删除集合中的一个元素 +print('aset: ',aset) +print('aset.pop() :',aset.pop()) +print('aset: ', aset) +``` + + aset: {1, 2, 3, 4, 5} + aset.pop() : 1 + aset: {2, 3, 4, 5} + + + +```python +#使用remove或discard删除集合中的指定的一个元素 +bset = {3,4,5} +print('bset: ',bset) +bset.remove(3) +print('bset: ', bset) +bset.discard(4) +print('bset: ', bset) +``` + + bset: {3, 4, 5} + bset: {4, 5} + bset: {5} + + +## 7 字典 +字典是一个无序的可以修改的序列,每一个元素都由一对key与value组成,key为对应value的索引。 + +key不可重复,且需要是不可变对象(可hash),可以是数字,字符串,元组等,但不能是列表。value可以重复 + +字典的创建可以用dict()或者{key1:value1,key2:value2,...}来创建。还记得在集合中我们提过{}能用于创建空集合,是因为{}创建的是一个空字典 + + +```python +adict = {"list" : [1,2,3],'list2':[1,2,3],'interLM': 'LLM', 1:'summer camp',2:'internLM'} +#访问key为list的元素的值 +print("adict['list'] :", adict['list']) +#修改key为list的元素的值 +adict['list'] = [3] +print("adict['list'] = [3]后的adict: ",adict) +#删除字典中的元素 +del adict['list2'] +print(adict) +``` + + adict['list'] : [1, 2, 3] + adict['list'] = [3]后的adict: {'list': [3], 'list2': [1, 2, 3], 'interLM': 'LLM', 1: 'summer camp', 2: 'internLM'} + {'list': [3], 'interLM': 'LLM', 1: 'summer camp', 2: 'internLM'} + + + +```python +#再来看看{}和dict()创建的空字典 +print("dict() ",type(dict())) +print("{} ",type({})) +``` + + dict() + {} + + +### 7.1 字典的常用方法 + +|方法名 | 描述 | +| --- | --- | +| dict.clear() | 删除字典内所有元素 | +| dict.copy() | 返回一个字典的浅复制 | +| dict.fromkeys() | 创建一个新字典,以序列seq中元素做字典的键,val为字典所有键对应的初始值 | +| dict.get(key, default=None) | 返回指定键的值,如果键不在字典中返回 default 设置的默认值 | +| dict.items() | 返回字典内所有的key value对| +| dict.keys() | 返回所有的key | +| dict.values() | 返回所有的values | +| dict.update(dict2) | 把字典dict2的键/值对更新到dict里 | +| pop(key[,default]) | 删除字典 key(键)所对应的值,返回被删除的值。| + +示例代码只会展示字典与列表不同的方法,对于名称与功能与列表相同的方法不再做展 + + +```python +#使用fromkeys创建key为[1,2,3,4] +dict.fromkeys([1,2,3,4],0) +``` + + + + + {1: 0, 2: 0, 3: 0, 4: 0} + + + + +```python +#使用get查询值,可以使用default参数设置当key不存在时的返回值 +print(adict.get("list",1)) +print(adict.get("list3")) +print(adict.get('list3',1)) +``` + + [3] + None + 1 + + + +```python +#使用items获取所有的key value对.是一个序列 +print(adict.items()) +``` + + dict_items([('list', [3]), ('interLM', 'LLM'), (1, 'summer camp'), (2, 'internLM')]) + + + +```python +#使用items获取所有的key +print(adict.keys()) +``` + + dict_keys(['list', 'interLM', 1, 2]) + + + +```python +#使用items获取所有的values +print(adict.values()) +``` + + dict_values([[3], 'LLM', 'summer camp', 'internLM']) + + +## 8 字符串 +学习LLM,必然逃不过对字符串的处理。 + +Python中的字符串使用`"`或者`'`来创建,两者等价,但是`"`会高于`'`。 + +此外,`'''`可以用来创建包含多行的字符串。 + +字符串本质上是一个不可变的元素可重复的有序序列,这也是为什么本教程会把字符串放在序列内容的最后一节。 + + +```python +astring = 'InternLM' +bstring = "Intern'LM'"#当字符串内有'时,可以使用" +print(list(astring)) +``` + + ['I', 'n', 't', 'e', 'r', 'n', 'L', 'M'] + + +字符串,本质上就是由字符组成的序列,将其转换为list就能看到他的原始数据结构,所以除了不能修改外,他的索引与切片操作跟列表一模一样。 + + +```python +#获取字符串中第3个元素 +print(astring[2]) +#获取字符串中倒数第二个元素 +print(astring[-2]) +#获取字符串中的第二个到第四个字符组成的子串 +print(astring[1:4]) +``` + + t + L + nte + + +### 8.1 字符串的运算符 +| 运算符 | 描述 | 例子 | +| --- | --- | ---| +|`+` | 字符串连接 || +|`*`| 重复输出字符串 | | +|`in` | 成员运算符 如果字符串中包含给定的字符返回 True| | +|`not in` | 成员运算符如果字符串中不包含给定的字符返回 True|'M' not in a 输出结果 True| + +### 8.2 字符串的方法 + +| 方法名 | 功能描述 | +| --- | --- | +| capitalize() | 将字符串的第一个字符转换为大写 | +| center(width, fillchar)|返回一个指定的宽度 width 居中的字符串,fillchar 为填充的字符,默认为空格| +| count(str, beg= 0,end=len(string))|返回 str 在 string 里面出现的次数,如果 beg 或者 end 指定则返回指定范围内str 出现的次数 | +| endswith(suffix, beg=0, end=len(string))|检查字符串是否以 suffix 结束,如果 beg 或者 end 指定则检查指定的范围内是否以 suffix 结束,如果是,返回 True,否则返回 False。| +|expandtabs(tabsize=8)|把字符串 string 中的 tab 符号转为空格,tab 符号默认的空格数是 8 | +| find(str, beg=0, end=len(string)) |检测 str 是否包含在字符串中,如果指定范围 beg 和 end ,则检查是否包含在指定范围内,如果包含返回开始的索引值,否则返回-1| +| index(str, beg=0, end=len(string))|跟find()方法一样,只不过如果str不在字符串中会报一个异常| +| isalnum()|如果字符串至少有一个字符并且所有字符都是字母或数字则返 回 True,否则返回 False| +|isalpha()|如果字符串至少有一个字符并且所有字符都是字母或中文字则返回 True, 否则返回 False| +|isdigit()|如果字符串只包含数字则返回 True 否则返回 False..| +|islower()|如果字符串中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True,否则返回 False| +|isnumeric()|如果字符串中只包含数字字符,则返回 True,否则返回 False| +|isspace()|如果字符串中只包含空白,则返回 True,否则返回 False.| +|istitle()|如果字符串是标题化的(见 title())则返回 True,否则返回 False| +|isupper()|如果字符串中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True,否则返回 False| +|join(seq)|以指定字符串作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串| +| ljust(width[, fillchar])|返回一个原字符串左对齐,并使用 fillchar 填充至长度 width 的新字符串,fillchar 默认为空格。| +|lower()|转换字符串中所有大写字符为小写| +|lstrip()|截掉字符串左边的空格或指定字符| +|maketrans()|创建字符映射的转换表,对于接受两个参数的最简单的调用方式,第一个参数是字符串,表示需要转换的字符,第二个参数也是字符串表示转换的目标。| +|max(str)|返回字符串 str 中最大的字母| +|min(str)|返回字符串 str 中最小的字母| +|replace(old, new [, max])|将字符串中的 old 替换成 new,如果 max 指定,则替换不超过 max 次| +|rfind(str, beg=0,end=len(string))|类似于 find()函数,不过是从右边开始查找.| +|rindex( str, beg=0, end=len(string))|类似于 index(),不过是从右边开始| +|rjust(width,[, fillchar])|返回一个原字符串右对齐,并使用fillchar(默认空格)填充至长度 width 的新字符串| +|rstrip()|删除字符串末尾的空格或指定字符。| +|split(str="", num=string.count(str))|以 str 为分隔符截取字符串,如果 num 有指定值,则仅截取 num+1 个子字符串| +| splitlines([keepends])|按照行('\r', '\r\n', \n')分隔,返回一个包含各行作为元素的列表,如果参数 keepends 为 False,不包含换行符,如果为 True,则保留换行符。| +| startswith(substr, beg=0,end=len(string)) |检查字符串是否是以指定子字符串 substr 开头,是则返回 True,否则返回 False。如果beg 和 end 指定值,则在指定范围内检查。| +| strip([chars]) | 在字符串上执行 lstrip()和 rstrip()| +| swapcase() |将字符串中大写转换为小写,小写转换为大写 | +| title() | 返回"标题化"的字符串,就是说所有单词都是以大写开始,其余字母均为小写 | +| translate(table, deletechars="") |根据 table 给出的表(包含 256 个字符)转换 string 的字符, 要过滤掉的字符放到 deletechars 参数中| +|zfill (width)|返回长度为 width 的字符串,原字符串右对齐,前面填充0| +| isdecimal()| 检查字符串是否只包含十进制字符,如果是返回 true,否则返回 false| + +要注意的是,因为字符串是不可修改的对象,所以每个修改字符串的方法都是将修改后的字符串作为一个新对象返回。 + + +```python +#字符串的方法实在太多,在在这里我们只取几个比较常用的方法作为示例。 +text = "Success is not final, failure is not fatal: It is the courage to continue that counts." +#首先我们把这句话中所有的字母都转换为小写 +text = text.lower() +print(text) +#再用replace把所有的标点符号去掉 +text = text.replace(','," ").replace(':'," ").replace('.'," ") +print(text) +#再用split把这句话拆成词组成的列表 +word_list = text.split(" ") +print(word_list) +#再用join函数用,把这个列表拼会一个字符串 +print(','.join(word_list)) +``` + + success is not final, failure is not fatal: it is the courage to continue that counts. + success is not final failure is not fatal it is the courage to continue that counts + ['success', 'is', 'not', 'final', '', 'failure', 'is', 'not', 'fatal', '', 'it', 'is', 'the', 'courage', 'to', 'continue', 'that', 'counts', ''] + success,is,not,final,,failure,is,not,fatal,,it,is,the,courage,to,continue,that,counts, + + +### 8.3 字符串的格式化输出 + +在做LLM开发时避不开对字符串做格式化输出,即指定一个模板把变量放入模板中。接下来我们介绍两种常见的易于实现复杂格式化输出的方法。 + +第一种为使用字符串的.format()方法,并在在字符串中需要插入值的地方用{}代替,{}也可以加入变量名,方便赋值。 + + +```python +#我们用字典存储一个学生的信息,假设小明是实战营第二期的 +#我们需要在输出的时候将他转化成一句话 +#小明在12岁的时候参加了书生浦语实战营第二期, 最终获得了优秀学员。 +student = {'name':'小明','age':12,'class':"书生浦语实战营第二期",'grade':'优秀学员'} +string_templet = '{}在{}岁的时候参加了{},获得了{}。'#做一个模板 +print(string_templet.format(student['name'],student['age'],student['class'],student['grade'])) +#在模板中没有指定变量名时, .format方法就按照顺序来填入值下面来演示一下加入变量名后会有什么不同 +string_templet2 = "{name}在{age}岁的时候参加了{course},获得了{grade}。"#做一个模板 +print(string_templet2.format(grade=student['grade'],name=student['name'],age=student['age'],course=student['class'])) +#使用这种变量方法命名时, format就可以忽略format参数中的顺序了,对于一些特别长变量特变多的模板来说更清晰。 +``` + + 小明在12岁的时候参加了书生浦语实战营第二期,获得了优秀学员。 + 小明在12岁的时候参加了书生浦语实战营第二期,获得了优秀学员。 + + +第二种方法我们使用python在3.6推出的f-sting功能,只需在字符串开头加上f,该字符串中的{}中的python代码就会被评估。 + + +```python +student = {'name':'小明','age':12,'class':"书生浦语实战营第二期",'grade':'优秀学员'} +string_out = f"{student['name']}在{student['age']}岁的时候参加了{student['class']},获得了{student['grade']}。" +print(string_out) +print(f"{1+2}") +``` + + 小明在12岁的时候参加了书生浦语实战营第二期,获得了优秀学员。 + 3 + + +## 9 控制结构 + +python中的if语句为: + +```python +if condition_1: + statement_block_1 +elif condition_2: + statement_block_2 +else: + statement_block_3 +``` + +* 如果 "condition_1" 为 True 将执行 "statement_block_1" 块语句 +* 如果 "condition_1" 为False,将判断 "condition_2" +* 如果"condition_2" 为 True 将执行 "statement_block_2" 块语句 +* 如果 "condition_2" 为False,将执行"statement_block_3"块语句 + + +接下来我们用if语句完成一个成绩等级判定的功能: +* [90,100] A +* [80,90) B +* [70,80) C +* [60,70) D +* [0,60) F + + +```python +score = 80 +if score>=90: + print('A') +elif score>=80: + print('B') +elif score>=70: + print('C') +elif score>=60: + print('D') +else: + print('F') +``` + + B + + +## 10 循环 + +### 10.1 while语句 + +Python while循环语句语法 +```python +while condition: + statement +``` + +以上是while语句的形式,当condition为True时,while会执行statement然后再判断condition,直至condition变为False才会结束循环 + + +```python +#用while语句求100以内数字的和 +i=0 +res = 0 +while i<100: + res+=i + i+=1 +print(res) +``` + + 4950 + + +### 10.2 for 语句 + +Python中for语句的语法为 +```python +for in : + +else: + +``` + +for循环可以遍历任何可迭代的对象,比如一个列表。 + + +```python +for st in ['InternLM','LLM','transformer','Shanghai']: + print(st) +``` + + InternLM + LLM + transformer + Shanghai + + +提到for循环就不能不提经常与它一起出现的range(start,stop,step)函数,他会生成一个可迭代的对象,以step步长生成[start,stop)。可以只写一个stop,默认从0开始。 + + +```python +for i in range(5): + print(i) +``` + + 0 + 1 + 2 + 3 + 4 + + +### 10.3 列表推导式 + + +```python +#列表推导式是一种很方便的写法,但我们在这不作为重点,大家可以看着例子应该就能理解列表推导式的写法 +#找出1-100内能被3整除的数 +print([i for i in range(1,101) if i%3==0]) +``` + + [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99] + + +## 11 函数 +### 11.1 函数的定义方式 +python定义函数的关键字为def + +```python +def 函数名(参数列表): + 函数体 +``` + + +```python +#定义一个打招呼的函数,别忘了冒号 +def hello(name): + out = 'Hello '+name + print(out) +``` + + +```python +hello('InternLM2') +``` + + Hello InternLM2 + + + +```python +#注意,函数中定义的变量为局部变量,只有函数内能访问到,这就是变量的作用领域 +print(out) +#当我们尝试使用变量out时就会报错,提示out没有被定义 +``` + + + --------------------------------------------------------------------------- + + NameError Traceback (most recent call last) + + ~\AppData\Local\Temp\ipykernel_13796\2663579863.py in + 1 #注意,函数中定义的变量为局部变量,只有函数内能访问到,这就是变量的作用领域 + ----> 2 print(out) + 3 #当我们尝试使用变量out时就会报错,提示out没有被定义 + + + NameError: name 'out' is not defined + + +### 11.2 函数的参数传递 + +* 参数是可变对象时,传参时传递的是索引,在函数中修改该参数会作用于原对象 +* 参数是不可变对象时,传参时会自动复制,在函数中修改该参数不会作用于原对象 + + +```python +def update(number,alist): + number = 3 + alist[1] = 3 + print("number: ", number) + print("alist: ", alist) + print("id(number)",id(number)) + print("id(alist)",id(alist)) +a = 2 +b = [1,1,1] +print(id(a)) +print(id(b)) +update(a,b) +``` + + 140710873047488 + 2531815942344 + number: 3 + alist: [1, 3, 1] + id(number) 140710873047520 + id(alist) 2531815942344 + + +可以看到在update函数内修改了number和alist,函数外面的整数a没有被修改,但是列表b被修改了。 +这就是python函数在传参时候的特征导致的。通过使用id()函数可以获取对象在内存中的地址可以看到number与a的id不同,他们是两个对象了。但是alist与b的id相同,说明他们两在内存中指向的是同一个对象。 + diff --git a/docs/L0/Python/readme.md b/docs/L0/Python/readme.md new file mode 100644 index 000000000..4f0dee9a8 --- /dev/null +++ b/docs/L0/Python/readme.md @@ -0,0 +1,445 @@ + +# Python + +B站封面 + +# **Intro** + +欢迎来到书生浦语大模型实战营基础部分Python关卡,本关主要由以下四大块内容组成: + +- Conda虚拟环境 +- pip安装三方依赖包 +- VScode中的Python Debug +- Python基础语法 + +**学习完成后,完成由两个任务经过助教批改视为闯关成功**。 +| 任务类型 | 任务内容 | 预计耗时 | +| --- |---| ---| +|闯关任务|Leetcode 383| 15mins| +|闯关任务|Vscode连接InternStudio debug笔记| 15mins| + +任务具体描述见[task.md](./task.md)。 + +TIPS:本关内容覆盖较多,知识点较杂,如果有不清楚或者不懂的地方可以随时提问。同时如果觉得教程有问题的地方也请随时提出,让我们一起把本教程优化得更好,帮助更多的人走进大模型的世界。 + +# Ch1 Conda虚拟环境 +虚拟环境是Python开发中不可或缺的一部分,它允许你在不同的项目中使用不同版本的库,避免依赖冲突。Conda是一个强大的包管理器和环境管理器。 + +## 1.1 创建新环境 +首先,确保你已经安装了Anaconda或Miniconda。在开发机上已经安装好了conda,大家可以直接使用。创建虚拟环境时我们主要需要设置两个参数,一是虚拟环境的名字,二是python的版本。 +```shell +conda create --name myenv python=3.9 +``` +这将创建一个名为myenv的新环境,使用Python 3.9。 +在安装包时会有一步二次确认,填y就行。 + +## 1.2 环境管理 +要使用创建好的虚拟环境,我们需要先激活环境。激活我们刚刚创建的myenv环境的命令如下: +```shell +conda activate myenv +``` +如果要退出环境,回到默认环境,命令为 +```shell +conda deactivate +``` +其他常见的虚拟环境管理命令还有 +```shell +#查看当前设备上所有的虚拟环境 +conda env list +#查看当前环境中安装了的所有包 +conda list +#删除环境(比如要删除myenv) +conda env remove myenv +``` +## 1.3 安装虚拟环境到指定目录 +有时我们会遇到想将整个虚拟环境保存到制定目录来共享,比如在局域网内,或者在InternStudio的团队开发机间共享。此时我们可以把conda的虚拟环境创建到指定目录下。 +只需要在创建环境时使用`--prefix`参数制定环境所在的文件夹即可,比如我们想在/root/envs/路径下创建刚刚我们创建过得myenv(只做演示,不用操作)。 +```shell +conda create --prefix /root/envs/myenv python=3.9 +``` +其他操作就与直接在默认路径下创建新环境没有区别了。想要激活保存在制定目录下的conda虚拟环境也十分简单,直接将环境名替换成所在文件夹就行。 +```shell +conda activate /root/envs/myenv +``` +myenv这个文件夹里包含了整个虚拟环境,所以理论上将他直接拷贝到任意一台安装了conda的机器上都能直接激活使用,这也是在内网机器上做环境配置的一种效率较高的解决方案。 + + +# Ch2: 使用pip安装Python三方依赖包 +在Python开发中,安装和管理第三方包是日常任务。pip是Python官方的包管理工具,全称为“Python Package Installer”,用于方便地安装、升级和管理Python包。 +## 2.1 使用pip安装包 +注意在使用conda的时候,我们需要先激活我们要用的虚拟环境,再在激活的虚拟环境中,使用pip来安装包。pip安装包的命令为`pip install`。 +```shell +pip install somepackages +#比如我想安装pandas和numpy +pip install pandas numpy +#我们也可以指定安装的版本 +pip install numpy==2.0 +#除了==外,还可以用>,<,>=,>=来指定一个版本的范围 +``` + +## 2.2 安装requirement.txt +如果你有一个包含所有依赖信息的requirements.txt文件,可以使用`-r`一次性安装所有依赖。requirements.txt在各种开源代码中经常可以看到,里面描述了运行该代码所需要的包和对应版本。 +```shell +pip install -r requirements.txt +``` +比如以下就是我们接下来会接触到的LLM部署框架lmdeploy的requirements.txt 的一部分(只做展示,大家不用自行安装) +```plain text +accelerate>=0.29.3 +mmengine-lite +numpy<2.0.0 +openai +peft<=0.11.1 +transformers +triton>=2.1.0,<=2.3.1; +``` + +## 2.3 安装到指定目录 +为了节省大家的存储空间,本次实战营可以直接使用share目录下的conda环境,但share目录只有读权限,所以要安装额外的包时我们不能直接使用pip将包安装到对应环境中,需要安装到我们自己的目录下。 +这时我们在使用pip的时候可以使用--target或-t参数来指定安装目录,此时pip会将你需要安装的包安装到你指定的目录下。 +这里我们用本次实战营最常用的环境`/root/share/pre_envs/pytorch2.1.2cu12.1`来举例。 +```shell +#首先激活环境 +conda activate /root/share/pre_envs/pytorch2.1.2cu12.1 +#我们可以创建一个目录/root/myenvs,并将包安装到这个目录下 +mkdir /root/myenvs +pip install somepackage --target /root/myenvs +#注意这里也可以使用-r来安装requirements.txt +pip install -r requirements.txt --target /root/myenvs +``` +要使用安装在指定目录的python包,可以在python脚本开头临时动态地将该路径加入python环境变量中去 +```python +import sys + +# 你要添加的目录路径 +your_directory = '/root/myenvs' + +# 检查该目录是否已经在 sys.path 中 +if your_directory not in sys.path: + # 将目录添加到 sys.path + sys.path.append(your_directory) + +# 现在你可以直接导入该目录中的模块了 +# 例如:import your_module +``` + +# Ch3 **使用本地Vscode连接InternStudio开发机** +## 3.1 使用SSH连接开发机 +VSCode是由微软开发一款轻量级但功能强大的代码编辑器,开源且完全免费。它拥有丰富的插件生态系统、跨平台支持、以及内置的Git控制功能,为开发者提供了高效便捷的编码体验。 + +VScode下载地址:[Visual Studio Code - Code Editing. Redefined](https://code.visualstudio.com/) + +首先需要安装Remote-SSH插件 + +
+ Description of the image +
+ + +安装完成后进入Remote Explorer,在ssh目录下新建一个ssh链接 + + +
+ Description of the image +
+ +此时会有弹窗提示输入ssh链接命令,回车后还会让我们选择要更新那个ssh配置文件,默认就选择第一个就行(如果你有其他需要的话也可以新建一个ssh配置文件)。 + +
+ Description of the image +
+ +
+ Description of the image +
+ + +开发机的链接命令可以在开发机控制台对应开发机"SSH连接"找到,复制登录命令到vscode的弹窗中然后回车,vscode就会开始链接InternStudio的服务器,记得此时切回去复制一下ssh的密码,待会会用到。 + +
+ Description of the image +
+ +在新的弹窗中将ssh密码粘贴进去然后回车。随后会弹窗让选择远程终端的类型,这边我们的开发机是linux系统,所以选择linux就好。 + +
+ Description of the image +
+ +首次连接会进行一些初始化的设置,可能会比较慢,还请耐心等待。后面打开文件夹的时候可能会再需要输入密码,可以一直开着开发机的控制台不要关掉以备不时之需。 + + +看到左下角远程连接已经显示ssh连接地址`SSH:ssh.intern-ai.org.cn`,说明我们已经连接成功了。然后我们就可以像在本地使用vscode一样愉快的使用vscode在开发机上进行任何操作了。 + +
+ Description of the image +
+ +连接成功后我们打开远程连接的vscode的extensions,在远程开发机上安装好python的插件,后面python debug会用到。也可以一键把我们本地vscode的插件安装到开发机上。 + +
+ Description of the image +
+ +
+ Description of the image +
+ +## 3.2如何在Vscode中打开终端 + + +单击vscode页面下方有一个X和!的位置可以快速打开vscode的控制台,然后进入TERMINAL。 + +
+ Description of the image +
+ + + +`TIPS`:右上方的+可以新建一个TERMINAL。 + + +4. Debug +4.1 使用VSCode进行Python Debug + + + + +# Ch4 使用vscode连接开发机进行python debug + +VSCode是由微软开发一款轻量级但功能强大的代码编辑器,开源且完全免费。它拥有丰富的插件生态系统、跨平台支持、以及内置的Git控制功能,为开发者提供了高效便捷的编码体验。 + +VScode下载地址:[Visual Studio Code - Code Editing. Redefined](https://code.visualstudio.com/) + +## 4.1 什么是debug? + +当你刚开始学习Python编程时,可能会遇到代码不按预期运行的情况。这时,你就需要用到“debug”了。简单来说,“debug”就是能再程序中设置中断点并支持一行一行地运行代码,观测程序中变量的变化,然后找出并修正代码中的错误。而VSCode提供了一个非常方便的debug工具,可以帮助你更容易地找到和修复错误。 + + +## 4.2 **使用Vscode进行Python debug的流程** + +### 4.2.1 debug单个python文件 +```python +def range_sum(start,end): + sum_res = 0 + for i in range(start,end): + sum_res+=i + return sum_res + +if __name__ =="__main__": + print(range_sum(1,10)) +``` + +**Step1.安装Python扩展** + - 打开VSCode,点击左侧扩展栏,搜索Python,并安装。 + +**Step2配置调试** + - 打开你的Python文件,点击左侧活动栏的“运行和调试”图标。 + - 首次debug需要配置以下,点击“create a launch.json file”,选择python debugger后选择“Python File” config。 + +
+ Description of the image +
+ + - 可以直接编辑生成的launch.json文件,配置调试参数,比如添加config(Add Configuration)等。 +
+ Description of the image +
+ +新建python文件后我们如果想要运行,首先需要选择解释器。单击右下角的select interpreter,vsconde会自动扫描开发机上所有的python环境中的解释器。 + + +**Step3.设置断点** + +在代码行号旁边点击,可以添加一个红点,这就是断点(如果不能添加红点需要检查一下python extension是否已经正确安装)。当代码运行到这里时,它会停下来,这样你就可以检查变量的值、执行步骤等。该函数的核心代码在第6行,所以接下来我们在第四行这打上断点。 + +
+ Description of the image +
+ +**Step4.启动debug** + +点击VSCode侧边栏的“Run and Debug”(运行和调试),选择debug配置后点击绿色箭头(开始调试)按钮,或者按F5键。 + +
+ Description of the image +
+ + +**Step5.查看变量** + +当代码在断点处停下来时,你可以查看和修改变量的值。在“Run and Debug”侧边栏的“Variables”(变量)部分,你可以看到当前作用域内的所有变量及其值。 + +
+ Description of the image +
+ +**Step6.单步执行代码** + +你可以使用顶部的debug面板的按钮来单步执行代码。这样,你可以逐行运行代码,并查看每行代码执行后的效果。 + +
+ Description of the image +
+ +debug面板各按钮功能介绍: + +* `1`: continue: 继续运行到下一个断点 + +* `2`: step over:跳过,可以理解为运行当前行代码,不进入具体的函数或者方法。 + +* `3`: step into: 进入函数或者方法。如果当行代码存在函数或者方法时,进入代码该函数或者方法。如果当行代码没有函数或者方法,则等价于step over。 + +* `4`: step out:退出函数或者方法, 返回上一层。 + +* `5`: restart:重新启动debug + +* `6`: stop:终止debug + +**Step7.修复错误并重新运行** + +如果你找到了代码中的错误,可以修复它,然后重新运行debug来确保问题已经被解决。 + +通过遵循以上步骤,你可以使用VSCode的debug功能来更容易地找到和修复你Python代码中的错误。可以自己编写一个简单的python脚本,并尝试使用debug来更好的理解代码的运行逻辑。记住,debug是编程中非常重要的一部分,所以不要怕花时间在这上面。随着时间的推移,你会变得越来越擅长它! + +### 4.2.2 不同的断点 +在调试(Debug)过程中,断点(Breakpoint)允许程序员在程序的执行流程中设置暂停点。当程序运行到这些断点时,执行会暂时中断,使得我们可以检查此时程序的状态,包括变量的值、内存的内容等。断点为我们提供了一个观察程序运行细节的机会,从而帮助我们定位和解决程序中的错误或问题。在VSCode中,我们还可以设置条件断点,这样断点只有在满足特定条件时才会触发。 + +1. 普通断点:在代码行号左侧点击,添加断点。 +2. 条件断点:在断点标记上右键,选择条件断点(conditional breakpoint)。Vscode中单个程序常用的条件断点主要有三种类型。 +* 1. Expression 表达式:输入一个python表达式,每次触发断点时运行该表达式,当表达式的值为True时vscode会暂停执行。如x==10 +* 2. Hit count 触发计数:断点触发计数达到输入值的时候才会暂停运行 +* 3. Log Message 记录日志:触发该断点的时候在Debug console中输出指定信息,其实就是logpoint。需要输入要输出的信息,如果要用到表达式的话,可以使用{}将表达式括起来。比如每一次都要记录变量i的值可以写x={i}。 + +#### 表达式条件断点 +比如我们想让代码从i=end-1时停下来,就可以这样设置。 +现在断点处右键选择条件断点,或者先建立一个普通断点以后编辑断点。 + +
+ Description of the image +
+ +回车保存断点,然后运行debug,可以看到程序在i=9的时候停了下来,此时各变量的值如下 + +
+ Description of the image +
+ + +#### 触发计数条件断点 +如果我们想让sum_res+=i在运行5次后停下来,我们可以在这行设置这样的Hit count断点 + +
+ Description of the image +
+ +运行debug后,程序第一次暂停时各变量的状态为 + +
+ Description of the image +
+ +#### 记录日志条件断点 +每次触发时记录一下i的值 + +
+ Description of the image +
+ +运行debug后我们可以在debug console看到 +
+ Description of the image +
+ +## 4.3 在vscode使用命令行进行debug + +很多时候我们要debug的不止是一个简单的python文件,而是很多参数,参数中不止会有简单的值还可能有错综复杂的文件关系,甚至debug一整个项目。这种情况下,直接使用命令行来发起debug会是一个更好的选择。 + +### 4.3.1 vscode设置 + + +vscode支持通过remote的方法连接我们在命令行中发起的debug server。首先我们要配置一下debug的config。 + + +还是点击VSCode侧边栏的“Run and Debug”(运行和调试),单击"create a lauch.json file" + +
+ Description of the image +
+ +选择debugger时选择python debuger。选择debug config时选择remote attach就行,随后会让我们选择debug server的地址,因为我们是在本地debug,所以全都保持默认直接回车就可以了,也就是我们的server地址为localhost:5678。 + +
+ Description of the image +
+ +
+ Description of the image +
+ +
+ Description of the image +
+ + +配置完以后会打开配置的json文件,但这不是重点,可以关掉。这时我们会看到run and debug界面有变化,出现了debug选项。 + +
+ Description of the image +
+ +### 4.3.2 debug命令行 + +现在vscode已经准备就绪,让我们来看看如何在命令行中发起debug。如果没有安装debugpy的话可以先通过pip install debugpy安装一下。 + +```shell +python -m debugpy --listen 5678 --wait-for-client ./myscript.py +``` + +* `./myscript.py`可以替换为我们想要debug的python文件,后面可以和直接在命令行中启动python一样跟上输入的参数。记得要先在想要debug的python文件打好断点并保存。 + +* `--wait-for-client`参数会让我们的debug server在等客户端连入后才开始运行debug。在这就是要等到我们在run and debug界面启动debug。 + +先在终端中发起debug server,然后再去vscode debug页面单击一下绿色箭头开启debug。 + +
+ Description of the image +
+ +
+ Description of the image +
+ + +接下来的操作就和上面一样了。 + +
+ Description of the image +
+ +### 4.3.3 使用别名简化命令 + + +这边有个不方便的地方,python -m debugpy --listen 5678 --wait-for-client这个命令太长了,每次都打很麻烦。这里我们可以给这段常用的命令设置一个别名。 + + +在`linux`系统中,可以对 *~/.bashrc* 文件中添加以下命令 + +```shell +alias pyd='python -m debugpy --wait-for-client --listen 5678' +``` + +然后执行 + +```shell +source ~/.bashrc +``` + +这样之后使用 pyd 命令(你可以自己命名) 替代 python 就能在命令行中起debug了,之前的debug命令就变成了 + +```shell +pyd ./myscript.py +``` + + +# Ch5: Python基础 +本课程也提供一个简易的Python基础教程(内容较多,请前往[ch5_python_intro.md](./ch5_python_intro.md)浏览)。 \ No newline at end of file diff --git a/docs/L0/Python/task.md b/docs/L0/Python/task.md new file mode 100644 index 000000000..e74facca9 --- /dev/null +++ b/docs/L0/Python/task.md @@ -0,0 +1,21 @@ +# Python task + +### 任务概览 +| 任务类型 | 任务内容 | 预计耗时 | +| --- |---| ---| +|闯关任务|Leetcode 383(笔记中提交代码与leetcode提交通过截图)| 15mins| +|闯关任务|Vscode连接InternStudio debug笔记| 15mins| + +闯关作业总共分为两个任务,两个任务均完成视作闯关成功。 +请将作业发布到知乎、CSDN等任一社交媒体,将作业链接提交到以下问卷,助教老师批改后将获得 50 算力点奖励!!! +提交地址:https://aicarrier.feishu.cn/share/base/form/shrcnUqshYPt7MdtYRTRpkiOFJd + +### 任务一 +完成[Leetcode 383](https://leetcode.cn/problems/ransom-note/description/), 笔记中提交代码与leetcode提交通过截图 + + +### 任务二 +使用VScode连接开发机,用任务一的代码走一遍debug的流程并做笔记 + +### 任务三(可选) +使用VScode连接开发机后使用`pip install -t`命令安装一个numpy到看开发机`/root/myenvs`目录下,并成功在一个新建的python文件中引用。 \ No newline at end of file