python the third week
文件操作
文件的概念
打开文件的方式
| 方式1: |
| open() 方法 |
| Python open() 方法用于打开一个文件,并返回文件对象。 |
| 在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError。 |
| 注意:使用 open() 方法一定要保证关闭文件对象,即调用 close() 方法。 |
| open() 函数常用形式是接收两个参数:文件名(file)和模式(mode)。 |
| eg: |
| f = open(文件路径,读写方式,encoding='utf8') |
| f.close() |
| 方式2: |
| 使用with方法 |
| with open (文件路径,读写方式,encoding='utf8') as 变量名: |
| ps:with上下文管理可以自己子代码结束后自动调用close方法关闭资源 |
| open方法的第一个参数是文件路径 并且撬棍跟一些字母的组合会产生特殊的含义导致路径查找混乱 为了解决该问题可以在字符串的路径前面加字母r |
| D:\a\n\t |
| r'D:\a\n\t' |
| 以后涉及到路径的编写 推荐加上r |
| |
| with支持一次性打开多个文件 |
| with open() as 变量名1,open() as 变量名2,open() as 变量名3: |
| 子代码 |
文件的读写操作
| 'r' 只读模式:以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 |
| 1文件路径不存在:会直接报错 |
| with open(r'b.txt','r',encoding='utf8')as f: |
| print(f.read()) |
| 2文件路径存在:正常读取文件的内容 |
| with open(r'a.txt','r',encoding='utf8')as f: |
| print(f.read()) |
| |
| 'w' 只写模式:打开一个文件只用于写入 |
| 1如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。 |
| with open(r'a.txt','w',encoding='utf8')as f: |
| f.write('study hard and make progress every day\n') |
| f.write('study hard and make progress every day\n') |
| f.write('study hard and make progress every day\n') |
| 2如果该文件不存在,创建新文件。 |
| with open(r'a.txt','w',encoding='utf8')as f: |
| pass |
| 'a' 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 |
| 1文件路径不存在:自动创建文件 |
| with open(r'b.txt','a',encoding='utf8')as f: |
| pass |
| 2文件路径存在:自动在末尾追加内容 |
| with open (r'a.txt','a',encoding='utf8')as f: |
| f.write('很开心见到了你') |
| |
| 当我们在编写代码的时候 有些部分不知道写什么具体代码 但是也不能空着不写这个时候可以使用关键字。 |
| pass |
| ... |
| 只补全语法不执行功能 本身没有任何的含义 |
文件的操作模式
| t 文本模式 |
| 默认的模式,即上面的r w a其实全称是 rt wt at |
| 1.只能操作文本文件. |
| 2.读写都是以字符为单位。 |
| 3.需要指定encoding参数 如果不知道则会采用计算机默认的编码. |
| |
| b 二进制模式(byte模式) |
| 这个需要自己进行指定eg: rb wb ab |
| 1.可以操作任意类型的文件. |
| 2.读写都是以bytes为单位. |
| 3.不需要指定encoding参数 因为它已经是二进制模式了 不需要编码. |
| |
| 二进制模式与文本模式针对文件路径是否存在的情况下 规律是一样的!!! |
文件的诸多方法
| 1.read() |
| 一次性读取文件内容 并且光标停留在文件末尾 继续读取则没有内容 |
| 并且当文件内容比较多的时候 该方法还可能会造成计算机内存溢出 |
| 括号内还可以填写数字 在文本模式下 表示读取几个字符如果未给定或为负则读取所有。 |
| 2for循环 |
| 一行行读取文件内容 避免内存溢出现象的产生 |
| 3readline([size]) |
| 读取整行,包括 "\n" 字符。 |
| 4readlines() |
| |
| 读取所有行并返回列表,若给定sizeint>0,返回总和大约为sizeint字节的行, 实际读取值可能比 sizeint 较大, 因为需要填充缓冲区。 |
| 5readline() |
| 判断文件是否具备读数据的能力会返回true和false |
| 6write() |
| 将字符串写入文件,返回的是写入的字符长度。 |
| 7witeable() |
| 判断文件是否具备写数据的能力true和false |
| 8writeline() |
| 向文件写入一个序列字符串列表,如果需要换行则要自己加入每行的换行符。 |
| 9flush() |
| 刷新文件内部缓冲,直接把内部缓冲区的数据立刻写入文件, 而不是被动的等待输出缓冲区写入。 |
文件光标的移动
| with open(r'a.txt','rb')as f: |
| print(f.read()) |
| f.seek(0,0) |
| print(f.read()) |
| f.seek(0,0) |
| print(f.read()) |
| print(f.tell()) |
| seek(offset,whence) |
| offset 是位移量 以字节为单位 |
| whence 是模式 0 1 2 |
| 0是基于文件开头:文本和二进制模式都可以用 |
| 1是基于当前位置:二进制模式可以用 |
| 2是基于文件末尾:二进制模式可以用 |
| 使用encode 编码 |
| decode 解码 |
文件光标移动案例
| import time |
| with open('access.log',mode='rb') as f: |
| f.seek(0,2) |
| while True: |
| line=f.readline() |
| if len(line) == 0: |
| |
| time.sleep(0.5) |
| else: |
| print(line.decode('utf-8'),end='') |
计算机硬盘修改数据的原理
| 硬盘写数据可以看成是在硬盘上刻字 一旦需要修改中间内容 则需要重新刻字 |
| 因为刻过的字不可能从中间再分开 |
| 硬盘删除数据的原理 |
| 不是直接删除而是改变状态 等待后续数据的覆盖才会被真正删除 |
文件内容修改
| |
| 张一蛋 山东 179 49 12344234523 |
| 李二蛋 河北 163 57 13913453521 |
| 王全蛋 山西 153 62 18651433422 |
| ''' |
| with open('a.txt', mode='r+', encoding='utf-8') as f: |
| # f.seek(9, 0) |
| f.seek(9) # seek模式就是0模式,第二个参数可以不指定 |
| f.write('<男妇女主任>') |
| |
| ''' |
| |
| 张一蛋<男妇女主任>9 49 12344234523 |
| 李二蛋 河北 163 57 13913453521 |
| 王全蛋 山西 153 62 18651433422 |
| 文件对应的是硬盘空间,硬盘不能修改对应着文件本质也不能修改, |
| 那我们看到文件的内容可以修改,是如何实现的呢? |
| 大致的思路是将硬盘中文件内容读入内存,然后在内存中修改完毕后再覆盖回硬盘 |
| 具体的实现方式分为两种: |
文件修改方式
| 文件修改方式1 |
| |
| |
| |
| with open('db.txt',mode='rt',encoding='utf-8') as f: |
| data=f.read() |
| |
| with open('db.txt',mode='wt',encoding='utf-8') as f: |
| f.write(data.replace('kevin','SB')) |
| 文件修改方式2 |
| |
| |
| |
| import os |
| |
| with open('db.txt',mode='rt',encoding='utf-8') as read_f,\ |
| open('.db.txt.swap',mode='wt',encoding='utf-8') as wrife_f: |
| for line in read_f: |
| wrife_f.write(line.replace('SB','kevin')) |
| |
| os.remove('db.txt') |
| os.rename('.db.txt.swap','db.txt') |
函数
函数前戏
| name_list = ['jason', 'kevin', 'oscar', 'jerry'] |
| def my_len(): |
| count = 0 |
| for i in name_list: |
| count += 1 |
| print(count) |
| my_len() |
| 循环 |
| 相同的代码在相同的位置反复执行 |
| 函数 |
| 相同的代码在不同的位置反复执行 |
| ps:相同的代码不是真正一模一样而是可以通过传入的数据不同而做出不同的改变 |
| 函数相当于是工具(具有一定功能) |
| 不用函数 |
| 修理工需要修理器件要用锤子 原地打造 每次用完就扔掉 下次用继续原地打造 |
| 用函数 |
| 修理工提前准备好工具 什么时候想用就直接拿出来使用 |
| 在程序中,具备某一功能的‘工具’指的就是函数,‘事先准备工具’的过程即函数的定义,‘拿来就用’即函数的调用。 |
函数的语法结构
| 1语法 |
| Python 定义函数使用 def 关键字,一般格式如下: |
| def 函数名(参数列表): |
| 函数体 |
| |
| 1def: 定义函数的关键字; |
| 2函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名应该反映出函数的功能; |
| 3括号:括号内定义参数,参数是可有可无的,且无需指定参数的类型; |
| 4冒号:括号后要加冒号,然后在下一行开始缩进编写函数体的代码; |
| 5"""文档描述""": 描述函数功能,参数介绍等信息的文档,非必要,但是建议加上,从而增强函数的可读性; |
| 6函数体:由语句和表达式组成; |
| 7return 值:定义函数的返回值,return是可有可无的。 |
| 参数是函数的调用者向函数体传值的媒介,若函数体代码逻辑依赖外部传来的参数时则需要定义为参函数 |
| def my_min(x,y): |
| res=x if x < y else y |
| return res |
| 否则定义为无参函数 |
| def interactive(): |
| user=input('user>>: ').strip() |
| pwd=input('password>>: ').strip() |
| return (user,pwd) |
函数的定义与调用
| 1.函数在定义阶段只检测语法 不执行代码 |
| def func(): |
| pass |
| 函数体为pass代表什么都不做,称之为空函数。定义空函数通常是有用的,因为在程序设计的开始,往往是先想好程序都需要完成什么功能,然后把所有功能都列举出来用pass充当函数体“占位符”,这将使得程序的体系结构立见,清晰且可读性强。 |
| 2.函数在调用阶段才会执行函数体代码 |
| func() |
| 3.函数必须先定义后调用 |
| 4.函数定义使用关键字def函数调用使用>>>:函数名加括号 |
函数的分类
| 1.空函数 |
| 函数体代码为空 使用的pass或者...补全的 |
| 空函数主要用于项目前期的功能框架搭建 |
| def register(): |
| """注册功能""" |
| pass |
| 2.无参函数 |
| 定义函数的时候括号内没有参数 |
| def index(): |
| print('from index function') |
| 3.有参函数 |
| 定义函数的时候括号内写参数 调用函数的时候括号传参数 |
| def func(a): |
| print(a) |
函数的返回值
| 1.什么是返回值 |
| 调用函数之后返回给调用者的结果 |
| 2.如何获取返回值 |
| 变量名 赋值符号 函数的调用 |
| res = func() |
| 3.函数返回值的多种情况 |
| 3.1.函数体代码中没有return关键字 默认返回None |
| 3.2.函数体代码有return 如果后面没有写任何东西还是返回None |
| 3.3.函数体代码有return 后面写什么就返回什么 |
| 3.4.函数体代码有return并且后面有多个数据值 则自动组织成元组返回 |
| 3.5.函数体代码遇到return会立刻结束 |
函数的参数
| 形式参数 |
| 在函数定义阶段括号内填写的参数 简称'形参' |
| 实际参数 |
| 在函数调用阶段括号内填写的参数 简称'实参' |
| +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| """ |
| 形参与实参的关系 |
| 形参类似于变量名 在函数定义阶段可以随便写 最好见名知意 |
| def register(name,pwd): |
| pass |
| |
| 实参类似于数据值 在函数调用阶段与形参临时绑定 函数运行结束立刻断开 |
| register('jason',123) 形参name与jason绑定 形参pwd与123绑定 |
| """ |
| +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
函数参数之位置参数
| 位置参数指的是按顺序定义的参数,需要从两个角度去看: |
| 1位置形参 |
| 在定义函数时,按照从左到右的顺序依次定义形参,称为位置形参,凡是按照这种形式定义的形参都必须被传值 |
| def funcl(a,b,c):pass 代码行只有一行可以直接写在冒号后面 |
| 2位置实参 |
| 在调用函数时,按照从左到右的顺序依次定义实参,称为位置实参,凡是按照这种形式定义的实参会按照从左到右的顺序与形参一一对应 |
| funcl(1,2,3) |
| |
| |
| eg: |
| def a(a,b): |
| print(a,b) |
| a(1,2) |
| a(1) |
| a(1,2,3) |
| a(b=1,a=2) |
| a(b=1,2) |
| a(2,b=1) |
| a(1,a=2,b=3) |
| name='jason' |
| pwd=123 |
| a(name,pwd) |
| a(a=name,b=pwd) |
| 口诀: |
| 越短的越简单的越靠前 |
| 越长的越复杂的越靠后 |
| 但是遇到下列的情况除外 |
| 同一个形参在调用的时候不能多次赋值 |
默认参数
| 本质其实就是关键字形参(关键字实参上述内容已经讲了) |
| 别名叫默认参数:提前就已经给了 用户可以不传 也可以传 |
| 定义时应遵循: |
| 默认参数的定义也遵循短的简单的靠前 长的复杂的靠后。 |
| def a(name,age,gender='male'): |
| print(""" |
| --------学员信息---------- |
| 姓名:{name} |
| 年龄:{age} |
| 性别:{gender} |
| ------------------------- |
| """) |
| a('tom',18) |
| a('jerk',28,'female') |
| a('jerry',10,gender="female") |
可变长形参
| 一个函数能处理比当初声明时更多的参数。这些参数叫做可变长参数 |
| 基本语法如下: |
| def functionname([formal_args,] *var_args_tuple ): |
| "函数_文档字符串" |
| function_suite |
| return [expression] |
| 加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。 |
| def a(*args): |
| print(args) |
| a() |
| a(1) |
| a(1, 2) |
| def a(**kwargs): |
| print(kwargs) |
| a() |
| a(a=1) |
| a(a=1, b=2) |
| 混合使用: |
| def a(*args, **kwargs): |
| print(args, kwargs) |
| |
| |
| a() |
| a(a=1) |
| a(a=1, b=2, c=3) |
| a(a=1, b=2, c=3, x='jason', y='kevin') |
| |
| def a(n, *args, **kwargs): |
| print(n, args, kwargs) |
| |
| |
| |
| a(1, 2, 3) |
| a(111, a=1, b=2, c=3) |
| a(n=111, a=1, b=2, c=3) |
| a(a=1, b=2, c=3, n=111) |
| a(1, 2, 3, a=1, b=2, c=3) |
| 注意: |
| 由于*和**在函数的形参中使用频率很高 后面跟的变量名推荐使用 |
| *args |
| **kwargs |
| def index(*args,**kwargs):pass |
可变长实参
| def a(a, b, c): |
| print(a, b, c) |
| |
| |
| l1 = [123, 321, 132] |
| |
| a(l1[0], l1[1], l1[2]) |
| |
| a(*l1) |
| l2 = ['aaa', 'bbb', 'ccc'] |
| a(*l2) |
| s1 = 'tom' |
| a(*s1) |
| set1 = {123, 1231, 321} |
| a(*set1) |
| dic1 = {'username': 'jason', 'pwd': 123, 'age': 18} |
| a(*dic1) |
| a(*dic1.values()) |
| *在实参中 |
| 类似于for循环 将所有循环遍历出来的数据按照位置参数一次性传给函数 |
| def user_info(name, password, age): |
| print(name, password, age) |
| |
| |
| d1 = {'name': 'mingming', 'password': 123, 'age': 18} |
| user_info(name=d1['name'], password=d1['password'], age=d1['age']) |
| user_info(*d1.values()) |
| user_info(**d1) |
| user_info(name='mingming', password=123, age=18) |
| **在实参中 |
| 将字典打散成关键字参数的形式传递给函数 |
| def a(*args, **kwargs): |
| print(args, kwargs) |
| a(*[11, 22, 33, 44]) |
| a(*(11, 22, 33, 44)) |
命名关键参数
| 形参必须按照关键字参数传值>>>:命名关键字参数 |
| def a(name, *args, gender='male', **kwargs): |
| print(name, args, gender, kwargs) |
| |
| |
| a('mingming', 1, 2, 3, 4, 5, a=1, b=2) |
| a('leilei', 1, 2, 2, 2, gender='female', a=1, b=2) |
名称空间与作用域
名称空间
| name = 'jason' |
| 1.申请内存空间存储jason |
| 2.给jason绑定一个变量名name |
| 3.后续通过变量名name就可以访问到jason |
| 名称空间即存放名字与对象映射/绑定关系的地方。对于x=3,Python会申请内存空间存放对象3,然后将名字x与3的绑定关系存放于名称空间中,del x表示清除该绑定关系。 |
| 在程序执行期间最多会存在三种名称空间 |
1.内置名称空间
| py文件执行过程中产生的名字都会存放于该名称空间中 |
| import sys |
| x=1 |
| if x == 1: |
| y=2 |
| def foo(x): |
| y=1 |
| def bar(): |
| pass |
| Class Bar: |
| pass |
名称空间存活周期及作用域
| 存活周期 |
| 内置名称空间 |
| python解释器启动则创建 关闭则销毁 |
| 全局名称空间 |
| py文件执行则创建 运行结束则销毁 |
| 局部名称空间 |
| 函数体代码运行创建 函数体代码结束则销毁(类暂且不考虑) |
| 作用域 |
| 内置名称空间 |
| 解释器级别的全局有效 |
| 全局名称空间 |
| py文件级别的全局有效 |
| 局部名称空间 |
| 函数体代码内有效 |
名字的查找顺序
| 1在python文件中相互独立的方法的命名空间默认是不能够互相访问的 |
| def a(): |
| name = 'jason' |
| print(age) |
| |
| def b(): |
| age = 18 |
| print(name) |
| 2局部空间嵌套 |
| 先从自己的局部名称空间查找 之后由内而外依次查找 |
| x = '123321' |
| def func1(): |
| x = 1 |
| def func2(): |
| x = 2 |
| def func3(): |
| x = 3 |
| print(x) |
| func3() |
| print(x) |
| func2() |
| print(x) |
| func1() |
| print(x) |
global与nonlocal
| 1 global关键字: |
| 在函数中,如果想给全局变量赋值,则需要用关键字global声明。 |
| eg: |
| money = 666 |
| def index(): |
| global money |
| money = 123 |
| index() |
| print(money) |
| 2 nonlocal关键字: |
| nonlocal只能在封装函数中使用,在外部函数先进行声明,在内部函数进行nonlocal声明 |
| def index(): |
| name = 'jason' |
| def inner(): |
| nonlocal name |
| name = 'kevin' |
| inner() |
| print(name) |
| index() |
函数名的多种用法
| 函数名其实绑定的也是一块内存地址 只不过该地址里面存放的不是数据值而是一段代码 函数名加括号就会找到该代码并执行. |
| 1.函数名可以当作变量名赋值 |
| def index():pass |
| res = index |
| res() |
| 2函数名可以当作函数的参数 |
| def index(): |
| print('from index') |
| def func(a): |
| print(a) |
| a() |
| func(index) |
| 3函数名可以当作函数返回值 |
| def index(): |
| print('from index') |
| def func(): |
| print('from func') |
| return index |
| res = func() |
| print(res) |
| res() |
| 4函数名可以当做容器类型(可以存放多个数据的数据类型)的数据。 |
| def reginster(): |
| print('注册功能') |
| def login(): |
| print('登录功能') |
| def withdraw(): |
| print('提现功能') |
| def transfer(): |
| print('转账功能') |
| def shopping(): |
| print('购物功能') |
| |
| fun_dict={ |
| '1':reginster, |
| '2':login, |
| '3': withdraw, |
| '4': transfer, |
| '5': shopping |
| } |
| while 1: |
| print(""" |
| 1.注册功能 |
| 2.登录功能 |
| 3.提现功能 |
| 4.转账功能 |
| 5.购物功能 |
| """) |
| choice =input('>>>:').strip() |
| if choice in fun_dict: |
| fun_name=fun_dict.get(choice) |
| fun_name() |
| else: |
| print('功能编号不存在') |
函数闭包
| 基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定了的,与函数的调用位置无关。 |
| 定义在函数内部的函数 并且用到了外部函数名称空间中的名字 |
| 1.定义在函数内容 |
| 2.用到外部函数名称空间中的名字 |
| def index(): |
| name = 'jason' |
| def inner(): |
| print(name) |
| 两种为函数体传值的方式,一种是直接将值以参数的形式传入,另外一种就是将值包给函数. |
| 方式1: |
| def register(name,age): |
| print(f""" |
| 姓名:{name} |
| 年龄:{age} |
| """) |
| register('jason', 18) |
| register(name='jason', age=18) |
| 方式2: |
| def outer(name, age): |
| |
| |
| def register(): |
| print(f""" |
| 姓名:{name} |
| 年龄:{age} |
| """) |
| return register |
| res = outer('jason', 18) |
| res() |
| res = outer('kevin', 28) |
| res() |
装饰器简介
| 1.概念 |
| 装饰器就是在不修改被装饰器对象源代码以及调用方式的前提下为被装饰对象添加新功能。 |
| 2本质 |
| 装饰器指的定义一个函数,该函数是用来为其他函数添加额外的功能.就是拓展原来函数功能的一种函数 |
| 3口诀 |
| 对修改封闭 对扩展开放 |
| 4操作 |
| 时间相关操作 |
| import time |
| print(time.time()) |
| time.sleep(3) |
| print('123321') |
| count = 0 |
| start_time=time.time() |
| while count<500: |
| print('qqqq') |
| count += 1 |
| end_time =time.time() |
| print('循环消耗的时间:', end_time - start_time) |
装饰器推导流程
| 1直接在调用index函数后面添加后代 |
| import time |
| def index(): |
| time.sleep(3) |
| print('from index') |
| def home(): |
| time.sleep(1) |
| print('from home') |
| start_time = time.time() |
| index() |
| end_time = time.time() |
| print('函数index的执行时间为>>>:', end_time-start_time) |
| 2index调用的地方较多 代码不可能反复拷贝>>>:相同的代码需要在不同的位置反复执行>>>:函数 |
| import time |
| def index(): |
| time.sleep(3) |
| print('from index') |
| def home(): |
| time.sleep(1) |
| print('from home') |
| def get_time(): |
| start_time = time.time() |
| index() |
| end_time = time.time() |
| print('函数index的执行时间为>>>:', end_time - start_time) |
| get_time() |
| 3..函数体代码写死了 只能统计index的执行时间 如何才能做到统计更多的函数运行时间 直接传参变换统计的函数 |
| import time |
| def index(): |
| time.sleep(3) |
| print('from index') |
| def home(): |
| time.sleep(1) |
| print('from home') |
| def get_time(xxx): |
| start_time = time.time() |
| xxx() |
| end_time = time.time() |
| print('函数的执行时间为>>>:', end_time - start_time) |
| get_time(index) |
| get_time(home) |
| 4.虽然实现了一定的兼容性 但是并不符合装饰器的特征 第一种传参不写 只能考虑闭包 |
| import time |
| def index(): |
| time.sleep(3) |
| print('from index') |
| def home(): |
| time.sleep(1) |
| print('from home') |
| def outer(xxx): |
| xxx = index |
| def get_time(): |
| start_time = time.time() |
| xxx() |
| end_time = time.time() |
| print('函数的执行时间为>>>:', end_time - start_time) |
| return get_time |
| res = outer(index) |
| res() |
| res1 = outer(home) |
| res1() |
| 5.调用方式还是不对 如何变形>>>:变量名赋值绑定 |
| import time |
| def index(): |
| time.sleep(3) |
| print('from index') |
| def home(): |
| time.sleep(1) |
| print('from home') |
| def outer(xxx): |
| def get_time(): |
| start_time = time.time() |
| xxx() |
| end_time = time.time() |
| print('函数的执行时间为>>>:', end_time - start_time) |
| return get_time |
| res = outer(index) |
| res1 = outer(index) |
| res2 = outer(index) |
| jason = outer(index) |
| index = outer(index) |
| index() |
| home = outer(home) |
| home() |
| 6.上述装饰器只能装饰无参函数 兼容性太差 |
| import time |
| def func(a): |
| time.sleep(0.1) |
| print('from func', a) |
| |
| def func1(a,b): |
| time.sleep(0.2) |
| print('from func1', a, b) |
| |
| def func2(): |
| time.sleep(0.3) |
| print('from func2') |
| func(123) |
| def outer(xxx): |
| def get_time(a, b): |
| start_time = time.time() |
| xxx(a, b) |
| end_time = time.time() |
| print('函数的执行时间为>>>:', end_time - start_time) |
| return get_time |
| func1 = outer(func1) |
| func1(1, 2) |
| func = outer(func) |
| func(1) |
| func2 = outer(func2) |
| func2() |
| 7.被装饰的函数不知道有没有参数以及有几个参数 如何兼容 |
| import time |
| def func(a): |
| time.sleep(0.1) |
| print('from func', a) |
| def func1(a,b): |
| time.sleep(0.2) |
| print('from func1', a, b) |
| def outer(xxx): |
| def get_time(*args, **kwargs): |
| start_time = time.time() |
| xxx(*args, **kwargs) |
| end_time = time.time() |
| print('函数的执行时间为>>>:', end_time - start_time) |
| return get_time |
| func = outer(func) |
| func(123) |
| func1 = outer(func1) |
| func1(1, 2) |
| 8.如果被装饰的函数有返回值 |
| import time |
| def func(a): |
| time.sleep(0.1) |
| print('from func', a) |
| return 'func' |
| def func1(a,b): |
| time.sleep(0.2) |
| print('from func1', a, b) |
| return 'func1' |
| def outer(xxx): |
| def get_time(*args, **kwargs): |
| start_time = time.time() |
| res = xxx(*args, **kwargs) |
| end_time = time.time() |
| print('函数的执行时间为>>>:', end_time - start_time) |
| return res |
| return get_time |
| func = outer(func) |
| res = func(123) |
| print(res) |
装饰器模板
| 在特定条件下为某些函数再不改动函数体的时候为函数新添加一些功能,这就是装饰器 |
| |
| def outer(func): |
| def inner(*args, **kwargs): |
| |
| res = func(*args, **kwargs) |
| |
| return res |
| return inner |
装饰器语法糖
| \@是一个装饰器,针对函数,起调用传参的作用。 |
| 有修饰和被修饰的区别,作为一个装饰器,用来修饰紧跟着的函数(可以是另一个装饰器,也可以是函数定义)。 |
| def outer(func_name): |
| def inner(*args, **kwargs): |
| print('执行被装饰对象之前可以做的额外操作') |
| res = func_name(*args, **kwargs) |
| print('执行被装饰对象之后可以做的额外操作') |
| return res |
| return inner |
| """ |
| 语法糖会自动将下面紧挨着的函数名当做第一个参数自动传给@函数调用 |
| """ |
| @outer |
| def func(): |
| print('from func') |
| return 'func' |
| @outer |
| def index(): |
| print('from index') |
| return 'index' |
| func() |
| index() |
多层语法糖
| def outter1(func1): |
| print('加载了outter1') |
| def wrapper1(*args, **kwargs): |
| print('执行了wrapper1') |
| res1 = func1(*args, **kwargs) |
| return res1 |
| return wrapper1 |
| |
| def outter2(func2): |
| print('加载了outter2') |
| def wrapper2(*args, **kwargs): |
| print('执行了wrapper2') |
| res2 = func2(*args, **kwargs) |
| return res2 |
| return wrapper2 |
| |
| def outter3(func3): |
| print('加载了outter3') |
| def wrapper3(*args, **kwargs): |
| print('执行了wrapper3') |
| res3 = func3(*args, **kwargs) |
| return res3 |
| return wrapper3 |
| |
| |
| @outter1 |
| @outter2 |
| @outter3 |
| def index(): |
| print('from index') |
| 多层语法糖加载的顺序从下往上执行, |
| 每次执行之后如果上面还有语法糖 则直接将返回值函数名传给上面的语法糖 |
| 如果上面没有语法糖了 则变形 index = outter1(wrapper2) |
有参装饰器
| def outter(x): |
| def outter1(func): |
| def wrapper(*args,*kwargs): |
| res = func(*args,**kwargs) |
| print(x) |
| return res |
| return wrapper |
| return outter1 |
| |
| |
| |
| |
| @outter('a') |
| |
| |
| |
| |
装饰器模板
| 最常用的无参装饰器 |
| def outer(func_name): |
| def inner(*args, **kwargs): |
| res = func_name(*args, **kwargs) |
| return res |
| return inner |
| @outer |
| def index(): |
| pass |
| |
| 不常用的有参装饰器 |
| def outer_plus(mode): |
| def outer(func_name): |
| def inner(*args, **kwargs): |
| res = func_name(*args, **kwargs) |
| return res |
| return inner |
| return outer |
| @outer_plus('MySQL') |
| def func(): |
| pass |
装饰器修复技术
| 由来:使用装饰器有一个小毛病,就是想要查看被装饰器的注释或者名字时,比如使用如下方法时: |
| print(help(index)) |
| print(index.__name__) |
| 结果看到的注释不是被装饰器的注释,而是装饰器里面的内部闭包函数的注释。 |
| 查看到的函数名字也是装饰器内部函数的名字,调用函数的时候,想看函数的名字和注释,看到的却不是对的。 |
| 基于以上情况,Python提供了解决方案,叫做装饰器修复技术。 |
| |
| |
| |
| |
| |
| from functools import wraps |
| def outer(func_name): |
| @wraps(func_name) |
| def inner(*args, **kwargs): |
| """我是inner 我擅长让人蒙蔽""" |
| res = func_name(*args, **kwargs) |
| return res |
| return inner |
| @outer |
| def func(): |
| """我是真正的func 我很强大 我很牛 我很聪明""" |
| pass |
| |
| |
| func() |
| |
递归函数
| 递归的特点: |
| 函数内部自己调用自己。 |
| 必须有出口。 |
| 1 必须有一个明确的结束条件。 |
| 2 每次进入更深一层递归时,问题规模(计算量)相比上次递归都应有所减少。 |
| 3 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出),此时程序会抛出错误:超出最大递归深度。 |
| |
| 递推:像上边递归实现所拆解,递归每一次都是基于上一次进行下一次的执行,这叫递推。 |
| 回溯:则是在遇到终止条件,则从最后往回返一级一级的把值返回来,这叫回溯。 |
| eg: |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| '''最大递归深度:python解释器添加的安全措施''' |
| |
| |
| |
| |
| |
| |
| |
| '''官网提供的最大递归深度为1000 我们在测试的时候可能会出现996 997 998''' |
| 2.递归函数 |
| """ |
| get_age(5) = get_age(4) + 2 |
| get_age(4) = get_age(3) + 2 |
| get_age(3) = get_age(2) + 2 |
| get_age(2) = get_age(1) + 2 |
| get_age(1) = 18 |
| """ |
| def get_age(n): |
| if n == 1: |
| return 18 |
| return get_age(n-1) + 2 |
| res = get_age(5) |
| print(res) |
算法简介及二分法
| 1.什么是算法 |
| 算法(Algorithm)是指解题方案的准确而完整的描述,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。如果一个算法有缺陷,或不适合于某个问题,执行这个算法将不会解决这个问题。不同的算法可能用不同的时间,空间或效率来完成同样的任务。一个算法的优劣可以用空间复杂度与时间复杂度来衡量。 |
| 2算法的特征 |
| 一个算法应该具有以下五个重要的特征: |
| 有穷性:算法的有穷性是指算法必须能在执行有限个步骤之后终止; |
| 确切性:算法的每一步骤必须有确切的定义; |
| 输入项:一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法本身定出了初始条件; |
| 输出项:一个算法有一个或多个输出,以反映对输入数据加工后的结果。没有输出的算法是毫无意义的; |
| 可行性:算法中执行的任何计算步骤都是可以被分解为基本的可执行的操作步骤,即每个计算步骤都可以在有限时间内完成(也称之为有效性)。 |
| 3算法应用场景 |
| 推荐算法(抖音视频推送 淘宝商品推送) |
| 成像算法(AI相关)...... |
| 几乎涵盖了我们日常生活中的方方面面 |
| 4算法部门 |
| 不是所有的互联网公司都养得起算法部分 只有大型互联网公司才有 |
| 算法部门类似于药品研发部分. |
| 5二分法 |
| 是算法中最简单的算法 甚至都称不上是算法 |
| 二分法使用要求: |
| 待查找的数据集必须有序 |
| 二分法的缺陷: |
| 针对开头结尾的数据 查找效率很低 |
| 常见算法的原理以及伪代码 |
| 二分法、冒泡、快拍、插入、堆排、桶排、数据结构(链表 约瑟夫问题 如 何链表是否成环) |
| l1 = [12, 21, 32, 43, 56, 76, 87, 98, 123, 321, 453, 565, 678, 754, 812, 987, 1001, 1232] |
| def get_middle(l1,target_num): |
| if len(l1)==0: |
| print('没找到') |
| return |
| minddle_index=len(l1)//2 |
| if target_num>l1[minddle_index]: |
| right_l1=l1[minddle_index+1:] |
| print(right_l1) |
| return get_middle(right_l1,target_num) |
| elif target_num<l1[minddle_index]: |
| left_l1=l1[:minddle_index] |
| print(left_l1) |
| return get_middle(left_l1,target_num) |
| else: |
| print('恭喜你 找到了!!!') |
| get_middle(l1,22) |
| |
| 冒泡 |
| 快拍 |
| 插入 |
| 堆排 |
| 桶排 |
| 数据结构(链表 约瑟夫问题 如 何链表是否成环) |
| |
| |
| |
| |
| |
| |
| |
三元表达式
| Python是示例可以看出,Python的三元表达式格式如下: |
| 条件为真时的结果 if 判段的条件 else 条件为假时的结果 |
| 适用场景:变量赋值时,要做条件判断时,简化代码时使用。 |
| a=1 |
| b=2 |
| 方法1 |
| c='qqq' if a<b else 'bbb' |
| print(c) |
| 数据值1 if 条件 else 数据值2 |
| 条件成立则使用数据值1 条件不成立则使用数据值2 |
| 当结果是二选一的情况下 使用三元表达式较为简便 |
| 并且不推荐多个三元表达式嵌套 |
| 方法2 |
| print({True: 'qqq', False: 'bbb'}[a > b]) |
| 模板: |
| {True:数据值1, False: 数据值2}[判断条件] |
| 条件成立则使用数据值1 条件不成立则使用数据值2 |
| 方法3 |
| print(('qqq','bbb')[a > b]) |
| 模板: |
| (数据值1,数据值2)[判断条件] |
| 条件不成立则使用数据值1条件成立则使用数据值2 |
| |
各种生成式/表达式/推导式
| Python 推导式是一种独特的数据处理方式,可以从一个数据序列构建另一个新的数据序列的结构体。 |
| Python 支持各种数据结构的推导式: |
| 列表(list)推导式 |
| 字典(dict)推导式 |
| 集合(set)推导式 |
| 元组(tuple)推导式 |
| 列表推导式格式: |
| [表达式 for 变量 in 列表] |
| [out_exp_res for out_exp in input_list] |
| 或者 |
| [表达式 for 变量 in 列表 if 条件] |
| eg: |
| [out_exp_res for out_exp in input_list if condition] |
| name_list=['jason','xiaoming','xiaoghong','xiaolan'] |
| |
| |
| new_list=[] |
| for name in name_list: |
| name=f'{name}_NB' |
| new_list.append(name) |
| print(new_list) |
| |
| new_list1=[name +'_NB' for name in name_list] |
| print(new_list1) |
| |
| new_list11=[name +'_NB' for name in name_list if name=='jason'] |
| print(new_list11) |
| new_list111=['嘻嘻'if name=='jason' else'哈哈' for name in name_list if name !='xiaoghong'] |
| print(new_list111) |
| 字典推导式: |
| 字典推导基本格式: |
| { key_expr: value_expr for value in collection } |
| 或 |
| { key_expr: value_expr for value in collection if condition } |
| |
| eg: |
| s1='hello word' |
| for i,j in enumerate(s1): |
| print(i,j) |
| d1={i:j for i,j in enumerate('hello')} |
| print(d1) |
| |
| enumerate() 方法的语法: |
| enumerate(sequence, [start=0]) |
| sequence -- 一个序列、迭代器或其他支持迭代对象。 |
| start -- 下标起始位置。 |
| enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。 |
| 集合推导式: |
| 集合推导式基本格式: |
| { expression for item in Sequence } |
| 或 |
| { expression for item in Sequence if conditional } |
| eg: |
| res = {i for i in 'hello'} |
| print(res) |
| 元组推导式 |
| 元组推导式可以利用 range 区间、元组、列表、字典和集合等数据类型,快速生成一个满足指定需求的元组。 |
| 元组推导式基本格式: |
| (expression for item in Sequence ) |
| 或 |
| (expression for item in Sequence if conditional ) |
| 元组推导式和列表推导式的用法也完全相同,只是元组推导式是用 () 圆括号将各部分括起来,而列表推导式用的是中括号 [],另外元组推导式返回的结果是一个生成器对象。 |
| 例如,我们可以使用下面的代码生成一个包含数字 1~9 的元组: |
| a=(b for b in range(1,10)) |
| print(tuple(a)) |
匿名函数
| Python 使用 lambda 来创建匿名函数。 |
| 所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。 |
| lambda 只是一个表达式,函数体比 def 简单很多。 |
| lambda 的主体是一个表达式,而不是一个代码块。仅仅能在 lambda 表达式中封装有限的逻辑进去。 |
| lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。 |
| 虽然 lambda 函数看起来只能写一行,却不等同于 C 或 C++ 的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。 |
| 语法 |
| lambda 函数的语法只包含一个语句,如下: |
| lambda [arg1 [,arg2,.....argn]]:expression |
| 设置参数 a 加上 10: |
| 实例 |
| x = lambda a : a + 10 |
| print(x(5)) |
| 以上实例输出结果: |
| 15 |
| 以下实例匿名函数设置两个参数: |
| 实例(Python 3.0+) |
| |
| |
| sum = lambda arg1, arg2: arg1 + arg2 |
| |
| print ("相加后的值为 : ", sum( 10, 20 )) |
| print ("相加后的值为 : ", sum( 20, 20 )) |
| 以上实例输出结果: |
| 相加后的值为 : 30 |
| 相加后的值为 : 40 |
| 我们可以将匿名函数封装在一个函数内,这样可以使用同样的代码来创建多个匿名函数。 |
| 以下实例将匿名函数封装在 myfunc 函数中,通过传入不同的参数来创建不同的匿名函数: |
| 实例 |
| def myfunc(n): |
| return lambda a : a * n |
| mydoubler = myfunc(2) |
| mytripler = myfunc(3) |
| print(mydoubler(11)) |
| print(mytripler(11)) |
| 以上实例输出结果: |
| 22 |
| 33 |
| |
内置函数
| 1map()映射 |
| map(func,iterable),其中func为函数名,可为lambda匿名函数,iterable为可迭代对象。此函数会将可迭代对象中的每一位元素作为参数传递到func中,并将func的计算结果加入到新列表内,map()返回的是一个包含所有结果的新列表。 |
| eg: |
| l=[1,2,3,4,5] |
| res=map(lambda x:x+1,l) |
| print(list(res)) |
| 2.max()\min() |
| ll=[11, 22, 33, 44] |
| res=max(ll) |
| print(res) |
| res1=min(ll) |
| print(res1) |
| d1 = { |
| 'zj': 100, |
| 'jason': 8888, |
| 'berk': 99999999, |
| 'oscar': 1 |
| } |
| def func(a): |
| return d1.get(a) |
| |
| res = max(d1, key=func) |
| print(res) |
| 3.reduce |
| |
| from functools import reduce |
| l1 = [11, 22, 33, 44, 55, 66, 77, 88] |
| res = reduce(lambda a, b: a * b, l1) |
| print(res) |
重要内置函数
| zip() 映射 |
| zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。 |
| 我们可以使用 list() 转换来输出列表。 |
| 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。 |
| l1=[11,22,33,44,55] |
| l2=['a','aa','aaa','aaaa','aaaaa'] |
| l3=[1,2,3,4,5] |
| res=list(zip(l1,l2,l3)) |
| print(res) |
| |
| |
| l1=[11,22,33,44,] |
| l2=['a','aa','aaa','aaaa','aaaaa'] |
| l3=[1,2,3,] |
| res=list(zip(l1,l2,l3)) |
| print(res) |
| |
| filter() 过滤序列 |
| 函数用于过滤序列,过滤掉不符合条件的元素,返回一个迭代器对象,如果要转换为列表,可以使用 list() 来转换。 |
| 该接收两个参数,第一个为函数,第二个为序列,序列的每个元素作为参数传递给函数进行判断,然后返回 True 或 False,最后将返回 True 的元素放到新列表中。 |
| 以下是 filter() 方法的语法: |
| filter(function, iterable) |
| l1=[11,22,33,44,55] |
| res=filter(lambda x:x>40,l1) |
| print(list(res)) |
| sorted 排序 |
| sorted() 函数对所有可迭代的对象进行排序操作。 |
| sort 与 sorted 区别: |
| sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。 |
| list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。 |
| sorted(iterable, key=None, reverse=False) |
| l1=[11,22,44,55,33] |
| res=sorted(l1) |
| print(list(res)) |
常见内置函数
| abs() 绝对值 |
| print(abs(-111)) |
| all() 所有数据值对应的布尔值为True结果才是True 否则返回False |
| print(all([0,1,2,3])) |
| print(all([1,2,3])) |
| any()所有数据值对应的布尔值有一个为True结果就是True 否则返回False |
| print(any([0,None,'',1])) |
| print(any([0, None, ''])) |
| bin() oct() hex() int() |
| bytes() 转换成bytes类型 |
| s1='好好学习 天天向上' |
| print(s1.encode('utf8')) |
| print(bytes(s1,'utf8')) |
| callable() |
| callable() 函数用于检查一个对象是否是可调用的。如果返回 True,object 仍然可能调用失败;但如果返回 False,调用对象 object 绝对不会成功。 |
| 对于函数、方法、lambda 函式、 类以及实现了 __call__ 方法的类实例, 它都返回 True |
| name='jason' |
| def index(): |
| print('from index') |
| print(callable(name)) |
| print(callable(index)) |
| chr() ord() 基于ASCII码做数字与字母转换 |
| print(chr(65)) |
| print(chr(90)) |
| print(chr(97)) |
| print(chr(122)) |
| dir() |
| dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法__dir__(),该方法将被调用。如果参数不包含__dir__(),该方法将最大限度地收集参数信息 |
| dir 语法: |
| dir([object]) |
| print(dir('hello')) |
| divmod() |
| python divmod() 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。 |
| res=divmod(100,2) |
| print(res) |
| res=divmod(13,2) |
| print(res) |
| paga,more=divmod(999,2) |
| if more: |
| paga+=1 |
| print('总页码',paga) |
| enumerate() 枚举 |
| enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。 |
| a=['aa','aa','aa','aa','aa','aa',] |
| a=enumerate(a) |
| print(list(a)) |
| eval() exec() |
| eval() 函数用来执行一个字符串表达式,并返回表达式的值 |
| 以下是 eval() 方法的语法: |
| eval(expression[, globals[, locals]]) |
| expression -- 表达式。 |
| globals -- 变量作用域,全局命名空间,如果被提供,则必须是一个字典对象。 |
| locals -- 变量作用域,局部命名空间,如果被提供,可以是任何映射对象。 |
| s2 = 'for i in range(100):print(i)' |
| |
| exec(s2) |
| hash() 哈希加密 |
| print(hash('123321')) |
| id() input() isinstance() |
| Python 中 id() 函数用于获取对象的内存地址。 |
| isinstance() |
| isinstance() 函数来判断一个对象是否是一个已知的类型,类似 type()。 |
| isinstance() 方法的语法: |
| isinstance(object, classinfo) |
| map() max() min() |
| open() |
| pow() |
| pow() 方法返回 xy(x 的 y 次方) 的值。 |
| print(pow(2,2)) |
| print(pow(2,3)) |
| print(pow(2,4)) |
| range() |
| 函数语法 |
| range(start, stop[, step]) |
| Python 3.x中不存在Python 2.x的xrange()。在Python 2.x中,range返回一个列表,即range(3)返回[0,1,2],而xrange返回一个xrange对象,即xrange(3)返回iterator对象,它与Java迭代器类似,并在需要时生成数字。 |
| 如果我们需要多次迭代相同的序列,我们更喜欢range(),因为range提供了一个静态列表。xrange()每次重建序列。xrange()不支持切片和其他列表方法。xrange()的优点是,当任务迭代大范围时,它可以节省内存。 |
| 在Python 3.x中,范围函数现在执行xrange在Python 2.x中的功能,因此为了保持代码的可移植性,我们可能希望坚持使用范围。所以Python 3.x的范围函数是来自Python 2.x的xrange。 |
| round() |
| round() 方法返回浮点数x的四舍五入值。 |
| round( x [, n] ) |
| x -- 数值表达式。 |
| n -- 数值表达式,表示从小数点位数。 |
| print "round(80.23456, 2) : ", round(80.23456, 2) |
| sum() |
| sum() 方法对序列进行求和计算。 |
| print(sum([11,22,33,44,555,666])) |
| zip() |
| zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。 |
| 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表。 |
| |
可迭代对象
| 1可迭代对象 |
| 对象内置有__iter__方法的都称为可迭代对象 |
| 1.1内置方法 通过点的方式能够调用的方法 |
| 1.2__iter__ 双下iter方法 |
| 2可迭代对象的范围 |
| 不可迭代的对象 |
| int float bool 函数对象 |
| 可迭代的对象 |
| str list dict tuple set 文件对象 |
| 3可迭代的含义 |
| 更新换代(每次更新必须依赖上一次的结果) |
| 手机更新 |
| 可迭代在python 中可以可以理解为是支持For循环 |
迭代器对象
| 1.迭代对象 |
| 是由可迭代对象调用__iter__方法产生的 |
| 迭代器对象判断的本质是看是否内置有__iter__和__next__ |
| 2.迭代器对象的作用 |
| 提供了一种不依赖于索引取值的方式 |
| 正因为有迭代器的存在 我们的字典 集合才能够被for循环 |
| 3.迭代器对象实操 |
| s1 = 'hello' |
| res = s1.__iter__() |
| print(res.__next__()) |
| 一旦__next__取不到值 会直接报错 |
| 4.注意事项 |
| 可迭代对象调用__iter__会成为迭代器对象 迭代器对象如果还调用__iter__不会有任何变化 还是迭代器对象本身 |
for循环内部原理
| for 变量名 in 可迭代对象: |
| 循环体代码 |
| 1.先将in后面的数据调用__iter__转变成迭代器对象 |
| 2.依次让迭代器对象调用__next__取值 |
| 3.一旦__next__取不到值报错 for循环会自动捕获并处理 |
| |
异常处理
| python提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。 |
| 异常就是代码运行报错 行业俗语叫bug |
| 代码运行中一旦遇到异常会直接结束整个程序的运行 我们在编写代码的过程中药尽可能避免 |
| 2.异常分类 |
| 语法错误 |
| 不允许出现 一旦出现立刻改正 否则提桶跑路 |
| 逻辑错误 |
| 允许出现的 因为它一眼发现不了 代码运行之后才可能会出现 |
| 3.异常结构 |
| 错误位置 |
| 错误类型 |
| 错误详情 |
| |
| |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下