周总结
周总结
- 文件操作
- 文件内光标移动案例
- 计算机硬盘修改数据的原理
- 文件内容修改
- 函数前戏
- 函数的语法结构
- 函数的定义与使用
- 函数的分类
- 函数的返回值
- 函数的参数
- 函数参数
- 名称空间与作用域
- 名字的查找顺序
- global与nonlocal
- 函数名的多种用法
- 闭包函数
- 装饰器简介
- 装饰器推导流程
- 装饰器模板
- 装饰器语法糖
- 多层语法糖
- 有参装饰器
- 装饰器修复技术
- 递归函数
- 算法简介及二分法
- 三元表达式
- 各种生成式
- 匿名函数
- 常见内置函数
- 重要内置函数
- 可迭代对象
- 迭代器对象
- for循环的本质
- 异常
文件操作
文件的概念
 就是操作系统暴露给用户操作硬盘的快捷方式(双击一个文件,其实就是从硬盘将数据加载到内存)
 ctrl+s保存文件,其实是将内存中的数据刷到硬盘保存
代码打开文件的两种方式
方式1:
    	f = open(文件路径,读写模式,encoding='utf8')
    	f.close()
方式2:
		
			with子代码块
		ps:with上下文管理好处在于子代码运行结束自动调用close方法关闭资源
with支持一次性打开多个文件:
    with open() as f1, open() as f2, open() as f3:
        子代码
'''
open方法的第一个参数是文件路径 并且撬棍跟一些字母的组合会产生特殊的含义导致路径查找混乱 为了解决该问题可以在字符串的路径前面加字母r
	D:\a\n\t
	r'D:\a\n\t'
'''
文件读写模式
只读模式
'r'  只能读不能写
with open(文件路径,'r',encoding='utf8') as 自定义的变量名:
	pass
    1.文件路径不存在:会直接报错
    2.文件路径存在:正常读取文件内容
只写模式
'w'  只能写不能看
with open(文件路径,'w',encoding='utf8') as 自定义的变量名:
	pass
    1.文件路径不存在:会自动创建
    2.文件路径存在:先清空文件内容 之后再写入
只追加模式
'a'  文件末尾添加数据
with open(文件路径,'a',encoding='utf8') as 自定义的变量名:
	pass
    1.文件路径不存在:会自动创建
    2.文件路径存在:自动在末尾追加内容
'''写入文件不会自动换行,需要自己添加换行符\r\n 并且在后续数据读取比对的时候也一定要注意他的存在
当我们在编写代码的时候 有些部分不知道写什么具体代码 但是也不能空着不写
这个时候可以使用关键字
	pass
	...
只补全语法不执行功能 本身没有任何的含义
'''
文件操作模式
文本模式
t    默认的模式 我们上面所写的r w a其实全称是 rt wt at
     1.只能操作文本文件
     2.读写都是以字符为单位
     3.需要制定encoding参数 如果不指定则会采用计算机默认的编码
二进制模式(bytes模式)
b    不是默认的模式 需要自己指定 rb wb ab
     1.可以操作任意类型的文件
     2.读写都是以bytes为单位
     3.不需要指定encoding参数 因为它已经是二进制模式了 不需要编码
二进制模式与文本模式针对文件路径是否存在的情况下 规律是一样的!!!
文件的诸多方法
1.read()
	一次性读取文件的内容 并且光标停留在文件末尾 继续读取则没有内容
   并且当文件内容较多的时候 该方法还可能会造成计算机内存溢出 
   括号内还可以填写数字 在文本模式下表示读取几个字符 
   在二进制模式下表示读取几个字节
2.for循环
	一行行读取文件内容 避免产生内存溢出的现象
3.readline()
	一次只读取一行
4.readlines()
	一次性读取文件内容 会按照行数组织成列表的一个个数据值
5.readable()
	判断文件是否具备读数据的能力
6.write()
	写入数据
7.writeable()
	判断文件是否具备写数据的能力
8.writelines()
	接收一个列表 一次性将列表中所有的数据值写入
9.flush()
	将内存中文件数据立刻刷到硬盘中 等同于ctrl + s
文件内光标移动
seek()
	seek(offset, whence)
        offset是位移量 以字节为单位
        whence是模式   0  1  2
            0是基于文件开头
                文本和二进制模式都可以使用
            1是基于当前位置
                只有二进制模式可以使用
            2是基于文件末尾
                只有二进制模式可以使用
tell()
	返回光标距离文件开头产生的字节数
文件内光标移动案例
'''监测文件是否有新加内容 有的话就打印出来'''
import time
with open(r'a.txt', 'rb') as f:
    f.seek(0, 2)  # 0表示偏移0个字节 2表示基于文件末尾
    while True:
        line = f.readline()
        if len(line) == 0:
            # 没有内容
            time.sleep(0.5)
        else:
            print(line.decode('utf8'), end='')
计算机硬盘修改数据的原理
		硬盘写数据可以看成是在硬盘上刻字 一旦需要修改中间内容 则需要重新刻字
因为刻过的字不可能从中间再分开
硬盘删除数据的原理
 不是直接删除而是改变状态 等待后续数据的覆盖才会被真正的删除
 比如: 你和老王去开房,住了一晚(数据写入),然后第二天就退房走了(数据删除),宾馆没有再安排客人入住(标记为空闲),如果想找到你和老王搞基的痕迹,那就再去你们那间房间里找就能找到(删除的数据可以被恢复),等宾馆再安排客人入住了你们的房间,那就再也找不到你们的证据了(数据被覆盖就没有办法恢复了)
文件内容修改的方式
方式1:覆盖写
	with open(r'a.txt', 'r', encoding='utf8') as f:
        data = f.read()
   with open(r'a.txt', 'w', encoding='utf8') as f1:
    	  f1.write(data.replace('jason', 'tony'))
方式2:换地写
	import os
	with open('a.txt', 'r', encoding='utf8') as f, open('a.txt.swap', 'w', encoding='utf8') as f1:
        for line in f:
            f1.write(line.replace('tony', 'kevin'))
'''先在另一个地方写入内容,然后将源文件删除 将新文件命名成源文件'''      
   os.remove('a.txt')   # 删除源文件
	os.rename('a.txt.swap', 'a.txt')  # 重命名文件
函数的前戏
neme_list = ['jason', 'kevin', 'oscar', 'jerry']
统计列表中有多少个数据值
	print(len(name_list))
当len不准使用时需要自己写代码
	count = 0
	for i in name_list:
		count += 1
	print(count)
当统计列表内数据值个数的代码需要在多处使用时就可以使用函数
函数
	相同的代码在不同的位置反复执行
'''相同的代码不是真正一模一样,而是可以通过传入的数据不同而做出不同的改变
'''
	def my_len():
		count = 0
		for i in name_list:
			count += 1
		print(count)
定义好后后续需要使用直接调用
	my_len()
'''
函数相当于是工具(具有一定功能)
	不用函数
		修理工修理器件需要使用锤子 原地打造 用完就扔 下次使用继续原地打造
	用函数
		修理工提前准备好工具 什么时候想用就直接拿出使用
'''
函数的语法结构
# 格式
def 函数名(参数)
	'''函数注释'''
	函数体代码
	return 返回值
# 具体解释
1.def
	定义函数的关键字
2.函数名
	命名等同于变量名,做到见名知意
3.参数
	可有可无 主要是在使用函数的时候规定要不要外界传数据进来
4.函数注释
	类似于工具说明书,解释该函数的作用及使用方法 可写可不写 最好写
5.函数体代码
	是整个函数的核心 主要取决于程序员的编写
6.return
	使用函数之后可以返回给使用者的数据 可有可无
函数的定义与使用
1.函数在定义阶段只检测语法 不执行代码
	def func():
		pass
2.函数在调用阶段才会执行函数体代码
	func()
3.函数必须先定义后调用
4.函数定义使用关键字def函数调用使用函数名加括号
	如果有参数则需要在括号内按照相应的规则传递参数
函数的分类
空函数
函数体代码为空 使用pass或...补全
空函数主要用于项目前期的功能框架搭建
def register():
    pass
无参函数
定义函数的时候括号内没有参数
def func():
    print('我是无参函数')
有参函数
定义函数的时候括号内写参数 调用函数的时候括号内传参数
def func1(a):
    print(a)
函数的返回值
返回值
- 调用函数之后返回给调用者的结果
获取返回值
变量名 赋值符号 函数的调用
# 示例
def func():
    print('我是函数')
    
res = func()   # 先执行func函数 然后将返回值赋值给变量res
print(res)   # None
函数返回值的多种情况
1.函数体代码中没有return关键字 默认返回None
2.函数体代码中有return关键字 如果后面没有写任何东西还是返回None
3.函数体代码中有return关键字 后面写什么就返回什么
4.函数体代码中有return关键字并且后面有多个数据值 则自动组织成元祖返回
5.函数体代码遇到return会立刻结束
函数的参数
形式参数
- 在函数定义阶段括号内填写的参数简称形参
实际参数
- 在函数调用阶段括号内填写的参数简称实参
"""
形参与实参的关系
	形参类似于变量名 在函数定义阶段可以随便写 最好见名知意
	def register(name,pwd):
		pass
	
	实参类似于数据值 在函数调用阶段与形参临时绑定 函数运行结束立刻断开
	register('jason',123)   形参name与jason绑定 形参pwd与123绑定
"""
函数参数
位置参数及关键字参数
位置形参
	函数定义阶段括号内从左往右依次填写的变量名
	def func(a, b, c):pass   # 括号内a b c 都是位置形参
'''当子代码只有一行并且很简单的情况下 可以直接在冒号后编写 不用换行'''
位置实参
	函数调用阶段括号内从左往右依次填写的数据值
	func(1, 2, 3)   # 按照位置传参  一一对应传值
    
关键字实参
	func(b=2, a=1, c=3)  # 传参数的时候指名道姓的传
'''传参注意事项'''
有多少个形参就要传多少个实参
def func1(a, b):
    print(a, b)
func(1)  # 少一个参数 会报错
func(1, 2, 3)  # 多一个参数 会报错
func(b=1, 2)  # 关键字传参一定要跟在位置传参后面 会报错
func(2, b=1)  # 关键字传参和位置传参正确写法
func(1, a=2, b=3)  # 同一个形参在调用的时候不能多次赋值 会报错
'''实参没有固定的定义 可以传数据值 也可以传绑定了数据值的变量名'''
name = 'cxk'
pwd = 123
func(name, pwd)  # 位置传参
func(a=name, b=pwd)  #关键字传参
'''
越短的越简单的越靠前
越长的越复杂的越靠后
但是遇到下列的情况除外
	同一个形参在调用的时候不能多次赋值
'''
默认参数
本质其实就是关键字形参别名叫默认参数:提前就已经给了 用户可以不传也可以传
'''默认参数的定义也遵循短的简单的靠前 长的复杂的靠后'''
def register(name, age, gender='male'):
    print(f'''
    ======信息======
    姓名:{name}
    年龄:{age}
    性别:{gender}
    ================
    ''')
    
register('cxk', 18)   # 因为有默认参数 所以可以不传gender参数
# register('cxk', 18, 'male')
register('lili', 18, 'female') # 也可以传gender参数 传新参数覆盖默认参数
register('lili', 18, gender='female')  # 通过关键字传参
可变长参数
可变长形参
*变量名 表示
'''*号在形参中用于接收多余的位置参数 组织成元祖赋值给*号后的变量名'''
def func(*a):
    print(a)
func()   # ()  没有传参也可以运行 
func(1)  # (1,) 
func(1, 2)  # (1, 2)
def func1(a, *b):
    print(a, b)
func1()  # 会报错 至少要传一个参数给到a
func1(1)  # 1, ()
func1(1, 2, 3)   # 1, (2, 3)
'''不建议的写法'''
def func2(*a, b):
    print(a, b)
func(1, 2)  # 会报错 所有参数都被a接收 b接收不到参数
func(b=1, 2)  # 会报错 位置参数必须在关键字参数前
func(1, b=2)  # (1,), 2
**变量名 表示
'''**号在形参中用于接收多余的关键字参数 组织成字典的形式赋值给**号后的变量名'''
def func3(**k):
    print(k)
func3()  # {}
func3(a=1)  # {'a': 1}
func3(a=1, b=2, c=3)  # {'a': 1, 'b': 2, 'c': 3}
def func4(a, **k):
    print(a, k)
func4()  # 报错 至少要一个参数给到a
func4(1) # 1 {}
func4(a=1) # 1 {}
func4(a=1, b=2, c=3)  # 1 {'b': 2, 'c': 3}
*和**结合使用
def func5(*a, **b):
	print(a, b)
func5()  # () {}
func5(1)  # (1,) {}
func5(a=1, b=2)  # () {'a':1, 'b': 2}
fucn5(1, 2, 3, a=4, b=5)  # (1, 2, 3) {'a':4, 'b': 5}
def func6(n, *a, **k):
	print(n, a, k)
func6()  # 至少需要一个参数给到n
func6(1, 2, 3)  # 1 (2, 3) {}
func6(111,a=1, b=2, c=3)  # 111 () {'a': 1, 'b': 2, 'c': 3}
func6(n=111,a=1, b=2, c=3)  # 111 () {'a': 1, 'b': 2, 'c': 3}
func6(a=1, b=2, c=3, n=111)  # 111 () {'a': 1, 'b': 2, 'c': 3}
func6(1, 2, 3, a=1, b=2, c=3)  # 1 (2, 3) {'a': 1, 'b': 2, 'c': 3}
'''
*和**在函数的形参中使用频率恒高所以后面跟的变量名推荐使用
	*args
	**kwargs
'''
可变长实参
'''*在实参中类似于for循环 将所有循环遍历出来的数据按照位置参数一次性传给函数'''
def fucn(a, b, c):
    print(a, b, c)
'''将列表中的三个数据值取出来传给函数的三个形参'''
l1 = [1, 2, 3]  # 列表中的数据值个数要和形参的个数相同不然报错
func(l1[0], l1[1], l1[2])  # 通过索引取值后传参 比较麻烦
func(*l1)   # 等同于func(1, 2, 3)   1 2 3
'''将元组中的三个数据值取出来传给函数的三个形参'''
t1 = (1, 2, 3)  # 元祖中的数据值个数要和形参的个数相同不然报错
func(t1[0], t1[1], t1[2])  # 通过索引取值后传参 比较麻烦
func(*t1)   # 等同于func(1, 2, 3)   1 2 3
'''将字符串中的三个数据值取出来传给函数的三个形参'''
s1 = 'cxk'   # 字符串中的字符个数要和形参的个数相同不然报错
func(*s1)  # 等同于func('c', 'x', 'k')  c x k
'''将集合中的三个数据值取出来传给函数的三个形参'''
se = {1, 2, 3}
func(*se)   # 集合是无序的 所以是不按顺序传参的
'''将字典中的三个数据值取出来传给函数的三个形参'''
d1 = {'num': 1, 'num1': 2, 'num2': 3}  # 通过*只会将key值传过去
func(*d1)   # 等同于func('num','num1', 'num2')
'''**在实参中是将字典打散成关键字参数的形式传递给函数'''
def func1(name, age):
    print(name, age)
d2 = {'name': 'cxk', 'age': 18}
func1(**d2)  # 等同于func1('name'='cxk', 'age'=18)
'''可变长形参和实参结合使用'''
def func2(*args, **kwargs):
    print(args)
    print(kwargs)
    
func2(*(1, 2, 3))  # (1, 2, 3)
				   # {}
命名关键字参数
'''形参必须按照关键字参数传值'''
def index(name, *args, gender='male', **kwargs):
    print(name, args, gender, kwargs)
index('jason',1,2,3,4,a=1,b=2)
# jason (1, 2, 3, 4) male {'a': 1, 'b': 2}
index('jason', 1, 2, 3, 4, 'female', b=2)
# jason (1, 2, 3, 4, 'female') male {'b': 2}
index('jason', 1, 2, 3, 4, a=1, gender='female', b=2)
# jason (1, 2, 3, 4) female {'a': 1, 'b': 2}
名称空间及它的存活周期和作用范围(域)
 名称空间就是用来存储变量名与数据值绑定关系的地方(可以理解为就是存储变量名的地方)
内置名称空间
解释器运行自动产生 里面包含了很多名字
如: len print input
存活周期
	python解释器启动则创建 关闭则销毁
作用域
	解释器级别的全局有效
全局名称空间
py文件运行产生 里面存放文件级别的名字
name = 'cxk'  
if name:
    age = 18
    
while True:
    gender = 'male'
    
def func():
    pass
class MyClass(object):
    pass
其中的name\age\gender\func\MyClass都存放在全局名称空间中
存活周期
	py文件执行则创建 运行结束则销毁
作用域
	py文件级别的全局有效
局部名称空间
函数体代码内运行\类体代码内运行产生的空间
def func():
    name = 'cxk'
    
class MyClass(object):
    name = 'jack'
    
其中各自的name都存放在函数体代码内运行\类体代码内运行产生的空间中
存活周期
	函数体代码运行创建 结束则销毁
作用域
	函数体代码内有效
名字的查找顺序
涉及到名字的查找 一定要先搞明白自己在哪个空间
1.当我们在局部名称空间中的时候
	局部名称空间 >>> 全局名称空间 >>> 内置名称空间
2.当我们在全局名称空间中的时候
	全局名称空间 >>> 内置名称空间
'''其实名字的查找顺序是可以打破的''' 
查找顺序案例
1.相互独立的局部名称空间默认不能够互相访问
	 def func1():
    	name = 'jason'
    	print(age)
    def func2():
       age = 18
       print(name)
2.局部名称空间嵌套
	先从自己的局部名称空间查找 之后由内而外依次查找
"""
函数体代码中名字的查找顺序在函数定义阶段就已经固定死了
	x = '干饭了'
    def func1():
        x = 1
        def func2():
            x = 2
            def func3():
                print(x)   # 会报错
                x = 3
            func3()
        func2()
    func1()
"""
global与nonlocal
'''通过global声明 可以通过局部名称空间直接修改全局名称空间中的数据'''
a = 100
def func():
	global a
	a = 888
func()
print(a)   # 不用global声明则打印全局名称空间中a的数据值 声明后运行函数会将新数据值赋予到全局名称空间中
'''通过nonlocal声明 可以通过内层局部名称空间修改外层局部名称空间中的数据值'''
def func1():
    b = 11
    def func2():
        nonlocal b
        b = 22
    func2()
    print(b)   # 22
    
func1()
函数名的多种用法
 函数名其实绑定的也是一块内存地址,只不过该内存地址里面存放的不是数据值而是一段代码,函数名加括号就会找到该代码并执行
当做变量名赋值
def func():pass
res = func
res()  # 将函数名绑定的内存地址再绑定给res 这样res()也能够运行原本的函数
当做函数的参数
def func():
    print('from func')
def func1(a):
    print('from func1')
    a()
func1(func)
当做函数的返回值
def func():
    print('from func')
def func1():
    print('from func1')
    return func
res = func1()
res()
# 嵌套使用
def index():
    print('from index')
    def func():
        print('from func')
    return func
res = index()
print(res)
res()
可以当做容器类型(可以存放多个数据的数据类型)的数据
def register():
    print('注册功能')
def login():
    print('登录功能')
def my_remove():
    print('删除功能')
def show():
    print('查看功能')
    
'''定义功能编号与功能的对应关系'''
func_dict = {
		'1': register,
		'2': login,
		'3': my_remove,
		'4': show
}
while True():
    print('''
		1.注册功能
		2.登录功能
		3.删除功能
		4.查看功能
		''')
		choice = input('请输入对应编号:')
		if choise in func_dict:
			func = func_dict.get(choice) # 字典通过k取对应的value值
			func() # 运行取到对应函数名的代码
      else:
			print('功能编号不存在')
'''用原本的方法需要一个一个写判断,当判断条件比较多时很麻烦'''
		if choice == '1':
			register()
		elif choice == '2':
			login()
		elif choice == '3':
			my_remove()
		elif choice == '4':
			show()  
      else:
         print('真多啊')   
闭包函数
'''
定义在函数内部的函数 并且用到了外部函数名称空间中的名字
	1.定义在函数内部的函数
	2.用到外部函数名称空间中的名字
'''
# 实例
def outer():
    age = 18
    def inner():
        print(age)
        # 内部函数中调用了外部函数中的变量age
        
闭包函数实际应用>>>>是另外一种给函数体代码传参的方式
'''给函数体代码传参'''
方式1:代码里面缺什么变量名形参中就补什么变量名
	def register(name, age):
        print(f'''
        姓名:{name}
        年龄:{age}
        ''')
	register('jack', 18)
    
方式2:闭包函数
	def outer(name, age):
		def register():
			print(f'''
			姓名:{name}
			年龄:{age}
			''')
		return register
	res = outer('jason', 18)
	res()
	res()   # 多次调用的时候不用重复传值
装饰器
装饰器简介
1.概念
	在不改变被装饰对象源代码和调用方式的情况下给被装饰对象添加新的功能
2.本质
	并不是一门新的技术,而是由函数参数、名称空间、函数名多种用法、闭包函数组合到一起的结果
3.口诀
	对修改封闭 对扩展开放
4.储备知识
	时间相关操作
	import time   # 导入time模块
	print(time.time())   # 时间戳(距离1970-01-01 00:00:00所经历的秒数)
	time.sleep(3)  # 代码运行到这停留三秒,括号内可以是任意数字
    
'''小案例'''
count = 0
while count < 100:
    start = time.time()  # 记录开始时间戳
    print('运行中')
    count += 1
end_time = time.time()   # 记录结束时间戳
print('运行消耗的时间:', end_time - start)
装饰器推导流程
'''定义函数,想要获取它运行的时长'''
import time
def func():
    time.sleep(3)
    print('from func')
'''1.直接在调用func函数的前后添加代码'''
start = time.time()
func()
end_time = time.time
print('函数func执行的时长为:', end_time - start)
'''2.当func函数调用的地方较多 代码不可能反复拷贝>>>相同的代码需要在不同的位置反复执行>>>再定义成函数'''
def get_time():
    start = time.time()
    func()
    end_time = time.time
    print('函数func执行的时长为:', end_time - start)
'''3.函数体代码写死了 只能统计func的执行时间 所以需要传参变化统计的函数'''
def get_time(a):
    start = time.time()
    a()
    end_time = time.time
    print('函数func执行的时长为:', end_time - start)
    get_time(func)
'''4.虽然实现了一定的兼容性 但是并不符合装饰器的特征  第一种传参不写 只能考虑闭包'''
def outer(a):
    def get_time():
        start = time.time()
        func()
        end_time = time.time
        print('函数func执行的时长为:', end_time - start)
    return get_time
res = outer(func)  # res是一个变量名 可以任意命名
res()
'''5.调用方式还是不对 如何变形>>>变量名赋值绑定'''
def outer(a):
    def get_time():
        start = time.time()
        func()
        end_time = time.time
        print('函数func执行的时长为:', end_time - start)
    return get_time
func = outer(func)  
# 括号内func是原本的函数名 左边的func其实就是get_time只是命名的原来的函数相同
func()
'''6.上述装饰器只能装饰无参函数 兼容性太差'''
def func1(a):
    time.sleep(0.1)
    print('from func1')
    
def func2(a, b):
    time.sleep(0.2)
    print('from func2')
    
def outer(a):
    def get_time(a):
        start = time.time()
        func()
        end_time = time.time
        print('函数func执行的时长为:', end_time - start)
    return get_time
fucn1 = outer(fucn1)
func1(1)
func2 = outer(func2)
func2(1, 2)  # 会报错 传的参数个数不对
'''7.被装饰的函数不知道有没有参数以及有几个参数 如何兼容'''
def func1(a):
    time.sleep(0.1)
    print('from func1')
    
def func2(a, b):
    time.sleep(0.2)
    print('from func2')
    
def outer(a):
    def get_time(*args, **kwargs):
        start = time.time()
        func(*args, **kwargs)
        end_time = time.time
        print('函数func执行的时长为:', end_time - start)
    return get_time
fucn1 = outer(fucn1)
func1(1)
func2 = outer(func2)
func2(1, 2)  # 这样不管传几个参数或者不传都不会报错
'''8.如果被装饰的函数有返回值'''
def func1(a):
    time.sleep(0.1)
    print('from func1')
    return 'func1'
    
def func2(a, b):
    time.sleep(0.2)
    print('from func2')
    
def outer(a):
    def get_time(*args, **kwargs):
        start = time.time()
        res = func(*args, **kwargs)
        end_time = time.time
        print('函数func执行的时长为:', end_time - start)
    return get_time
func1 = outer(func1)
res = func1(123)   # 将获取到的返回值定义给res
print(res)   # func1
装饰器模板
'''非常重要  要牢记'''
def outer(func):
	def inner(*args, **kwargs):
        # 执行被装饰对象之前可以做的额外操作
        res = func()
        # 执行被装饰对象之后可以做的额外操作
	return res
return inner
装饰器语法糖
@装饰器  就是语法糖
'''语法糖会自动将下面紧挨着的函数名当做第一个参数自动传给@函数调用'''
def outer(func):
	def inner(*args, **kwargs):
		print('执行被装饰对象之前可以做的额外操作')
		res = func()
		print('执行被装饰对象之后可以做的额外操作')
	return res
return inner
@outer  # func = outer(func) 相当于自动执行这行代码
def func():
    print('from func')
    return 'func'
func()
多层语法糖
'''
多层语法糖 加载顺序由下往上
每次执行之后如果上面还有语法糖 则直接将返回值函数名传给上面的语法糖
如果上面没有语法糖了 则变形index = outer1(inner2)
'''
def outer1(func1):
    print('执行了outer1')
    def inner1(*args, **kwargs):
        print('执行了inner1')
        res1 = func1(*args, **kwargs)
        return res1
    return inner1
def outer2(func2):
    print('执行了outer2')
    def inner2(*args, **kwargs):
        print('执行了inner2')
        res2 = func2(*args, **kwargs)
        return res2
    return inner2
def outer3(func3):
    print('执行了outer3')
    def inner3(*args, **kwargs):
        print('执行了inner3')
        res3 = func3(*args, **kwargs)
        return res3
    return inner3
@outer1
@outer2 
@outer3 
def index():
    print('from index')
index()
有参装饰器
'''当装饰器中需要额外的参数时 得使用有参装饰器'''
# 在外层再套用一个函数 用于接收额外的参数
'''示例'''
def outer(a): 
	def login_auth(func):
		def inner(*args, **kwargs):
			if a = '1':
				print("条件成立执行的代码")
				res = func(*args, **kwargs)
				return res
			elif a = '2':
				print("条件成立执行的代码")
				res = func(*args, **kwargs)
				return res
			elif a = '3':
				print("条件成立执行的代码")
				res = func(*args, **kwargs)
				return res
		return inner
	return login_auth
'''
函数名加括号先执行 优先级最高 有参装饰器的情况
	先看函数名加括号的执行
	然后再是语法糖的操作
'''
@outer('1')  # 在这给有参装饰器传参
def index():
    print('from index')
index()
装饰器模板
'''最常用的无参装饰器模板'''
def outer(func_name):
    def inner(*args, **kwargs):
        res = func_name(*args, **kwargs)
        return res
    return inner
@outer
def index():
    pass
'''不常用的有参装饰器模板'''
def outer_plus(a): 
	def outer(func_name):
		def inner(*args, **kwargs):
			res = func_name(*args, **kwargs)
			return res
		return inner
	return outer
@outer_plus('a')
def func():
    pass
装饰器修复技术
'''
当其他人不会用被装饰的函数时可能会用help查看,这时就会暴露 会显示出其实是执行了其他函数实现的 这时候就可以通过导入模块functools下的wraps实现 让其他人即使使用了help也不会暴露出真正执行的函数,而是显示被查看的函数
'''
# 使用模板
from functools import wraps
def outer(func_name):
    @wraps(func_name)  # 仅仅是为了让装饰器的效果更加逼真 平时可以不写
    def inner(*args, **kwargs):
        res = func_name(*args, **kwargs)
        return res
    return inner
递归函数
1.函数的递归调用
	函数直接或者间接的调用了函数自身
	'''直接调用'''
	def func():
        print('from func')
        func()  # 自身调用自身 会一直执行直到达到最大递归深度
	func()   
	'''间接调用'''
	def func1():
        print('from func1')
        func2()
	def func2():
        print('from func2')
        func1() 
	func1()
	'''最大递归深度:python解释器添加的安全措施 官网提供的最大递归深度为10001 我们在测试的时候可能会出现996 997 998'''
    
2.递归函数
	直接或者间接调用自己
	每次调用都必须必上一次简单 并且需要有一个明确的结束条件
		递归:一层层往下
		回溯:基于明确的结果一层层往上
        
'''递归函数案例  
求第一个人年龄 第二个人比第一个人小两岁 第三个人比第二个人小两岁 第四个人比第三个人小两岁 第五个人18岁比第四个人小两岁
'''
def get_age(n):
    if n == 1:
        return 18
    return get_age(n-1) + 2
res = get_age(5)
print(res)
算法简介及二分法
算法简介
1.什么是算法
	算法就是解决问题的有效方法 不是所有的算法都很高效 也有不合格的算法
2.算法应用场景
	推荐算法   如 抖音短视频 淘宝商品推送等等
	成像算法(AI相关)...
	几乎涵盖了我们日常生活中的方方面面
'''算法工程师的待遇非常好,但是他的要求也非常高,并且不是所有的互联网公司都养得起算法部门,通常情况下之后大型互联网公司才有,十年磨一剑'''
二分法
是算法中最简单的算法 甚至都称不上是算法
就是不断的对数据集做二分切割
'''
二分法使用要求
	待查找的数据集必须有序
缺陷
	针对在开头以及结尾的数据查找效率很低
'''
'''常见算法
二分法、冒泡、快拍、插入、堆排、桶排、数据结构(链表 约瑟夫问题 如何链表是否成环)
'''
代码实现二分法
# 二分法 不断的对数据集做二分切割
l1 = [12, 21, 32, 43, 56, 76, 87, 98, 123, 321, 453, 565, 678, 754, 812, 987, 1001, 1232]
# 查找列表中某个数据值
def get_num(a, num):
    # 添加一个结束条件
    if len(a) == 0:
        print('没找到')
    # 获取列表中间索引值
    middle = len(a) // 2
    # 比较目标数据值与中间索引值的大小
    if num < a[middle]:
        # 切片保留列表右边一半
        left_list = a[:middle]
        # 针对右边一半的列表继续二分并判断
        return get_num(new_list, num)
    elif num > a[middle]:
        # 切片保留列表左边一半
        right_list = a[middle + 1:]
        # 针对左边一半的列表继续二分并判断
        return get_num(new_list, num)
    else:
        print('找到了')
      
三元表达式
"""
数据值1 if 条件 else 数据值2
条件成立则使用数据值1 条件不成立则使用数据值2
当结果是二选一的情况下 使用三元表达式较为简便
并且不推荐多个三元表达式嵌套
"""
name = 'jason'
# 普通写法
if name == 'jason':
	print('老师')
else:
	print('学生')
    
# 三元表达式写法
res = '老师' if name == 'jason' else '学生'
print(res)
各种生成式/表达式/推导式
列表推导式
name_list = ['jason', 'kevin', 'oscar', 'tony', 'jerry']
# 给列表中所有人名的后面加上nb后缀
new_list = [name + 'nb' for name in name_list]
'''先看for循环 每次for循环之后再看for关键字前面的操作'''
# 复杂情况
new_list = [name + 'nb' for name in name_list if name == 'jason']
# 先看for循环 再看判断条件是否成立 成立再看最前面的操作
new_list = [name + 'nb' if name == 'jason' else name + 'sb' for name in name_list if name != 'jack']
# 先看for循环 再看判断条件是否成立 然后看前面的三元表达式 做判断 在执行条件成立的操作
字典生成式
d1 = {i: j for i,j in enumerate('hello')}
print(d1)  # {0: 'h', 1: 'e', 2: 'l', 3: 'l', 4: 'o'}
# enumerate函数会自动为被遍历后的数据依次添加从0开始的数字
集合生成式
s1 = {i for i in 'hello'}
print(s1)  # {'o', 'h', 'l', 'e'}  集合是无序的 
匿名函数
没有名字的函数 并且执行的代码比较简单 需要使用关键字lambda 一般不单独使用 需要配合其他函数一起使用
语法结构
	lambda 形参:返回值
如
	lambda a,b:a+b
常见内置函数
1.map() 映射
	map(func,iterable),其中func为函数名,可为lambda匿名函数,iterable为可迭代对象。此函数会将可迭代对象中的每一位元素作为参数传递到func中,并将func的计算结果加入到新列表内,map()返回的是一个包含所有结果的新列表。
    # 让字典中的每个数据值加1
    l1 = [1, 2, 3, 4, 5]
    # def func(a):
    #     return a + 1
	res = map(lambda x:x+1, l1)
	print(list(res)) # [2, 3, 4, 5, 6]
2.max()\min()  求最大值\求最小值
	min(iterable, *[, key, default])
参数:iterable是可迭代的对象,key指定寻找最小值的关键字,若key不指定则采用默认位置
返回值:返回值是iterable中寻找到的最小值
	l1 = [11, 22, 33, 44]
 	res = max(l1)
	print(res)  # 44
   
# 如果比较字典数据
	d1 = {
    'zj': 100,
    'jason': 8888,
    'berk': 99999999,
    'oscar': 1
	}
    def func(a):  # 定义一个函数,取对应的value值
        return d1.get(a)
    # res = max(d1, key=lambda k: d1.get(k)) 用匿名函数
    # 如果后面不指定参数 那么比较的是key值 而不是后面的value值
    res = max(d1, key=func)  # 用定义好的函数
    print(res) # 
    
3.reduce
	 # reduce  传多个值 返回一个值
    from functools import reduce  # reduce函数原本在python2中也是个内置函数,不过在python3中被移到functools模块中
    l1 = [11, 22, 33, 44, 55, 66, 77, 88]
    res = reduce(lambda a, b: a * b, l1)
    print(res)
重要内置函数
1.zip()
	zip 函数是可以接收多个可迭代对象,然后把每个可迭代对象中的第i个元素组合在一起,形成一个新的迭代器,类型为元组。
	# 两个可迭代对象数据值个数相同的情况下
	l1 = [1, 2, 3]
	l2 = ['jason', 'jack', 'tony']
	res = zip(l1, l2)
	# 此时的res是一个迭代器对象 需要转换类型才能看到具体的数据
	print(list(res))  # [(1, 'jason'), (2, 'jack'), (3, 'tony')]
   # 两个可迭代对象数据值个数不相同的情况下
	l3 = [1, 2, 3]
	l4 = ['jason', 'jack']
	res1 = zip(l1, l2)
	print(list(res1))  # [(1, 'jason'), (2, 'jack')] 多余的丢弃
    
3.filter
	filter 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
	语法:filter(function, iterable)
	参数:function是进行判断的条件,可以是匿名函数,function必须返回一个bool变量,当function判断为True时,该元素进行保留,当function判断为False时,该元素不保留。
	l5 = [11, 22, 33, 44 ,55]
	res2 = filter(lambda x:x > 30, new_list)
	# 特别需要注意到一点是在Python2.7版本中filter()返回列表,而在Python3.x版本中filter()返回迭代器对象,我们可以使用内置函数list()手动将filter迭代器对象转化为列表
	print(list(res2))  # [33, 44, 55]
    
4.sorted
	sorted 函数是对可迭代对象进行排序。
	语法:sorted(iterable, cmp=None, key=None, reverse=False)
	参数:
		iterable是待排序的可迭代对象。
		cmp是比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
		key是排序关键字,主要是用来进行比较的元素,只有一个参数,具体的函数的参	数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
		reverse是排序规则reverse = True 降序 , reverse = False 升序(默认)
	l6 = [33, 22, 55, 11, 88]
	res = sorted(l6)
	print(res)  # 默认是升序 [11, 22, 33, 55, 88]
常见内置函数
1.abs()
	其主要作用是对传入的参数,返回其的绝对量或绝对值
2.all()
	所有数据值对应的布尔值为True结果才是True 否则返回False
3.any()
	所有数据值对应的布尔值有一个为True结果就是True 否则返回False
4.bin() oct() hex() int()
	bin()转换为二进制 oct()转换为八进制 hex()转换为十六进制 int()转换为十进制
5.bytes()
	转换成bytes类型
6.callable()
	用于检查一个对象是否是可调用的。如果返回 True,仍然可能调用失败;但如果返回 False,调用对象绝对不会成功
7.chr() ord()
	基于ASCII码表做数字与字母的转换
	chr(数字) 返回值是对应的字符
	ord(字符) 返回值是对应的数字
8.dir()
	返回括号内对象能够调用的名字
9.divmod()
	返回元组 第一个数据为整除数 第二个是余数
	print(divmod(100, 3))  # (33, 1)
10.enumerate()
	返回一个元组,里面包含一个计数值(从 start 开始,默认为 0)和通过可迭代对象迭代获得的值
11.eval() exec()
	能够识别字符串中的python代码并执行
   eval() 只能识别简单的python代码 具有逻辑性的都不行
	exec() 可以识别具有一定逻辑性的python代码
12.hash()
	哈希加密
	用于获取取一个对象(字符串或者数值等的哈希值
13.isinstance()
	判断是否是一个已知类型
	a = 100
	res = isinstance(a, int)
	print(res)  # True
14.id() input() map() max() min() open() range() zip()
15.pow()
	返回一个数的次幂,可以利用pow函数计算一个数的平方,3次方等常见的操作
	print(pow(2, 2))  # 4
	print(pow(2, 3))  # 8
16.round()
	返回四舍五入值  # 其实是五舍六入
   print(round(98.3))  # 98
	print(round(98.6))  # 99
17.sum()
	对序列进行求和计算
可迭代对象
1.可迭代对象
	对象内置有__iter__方法的都称为可迭代对象
	'''
	内置方法 通过点的方法能够调用的方法
	__iter__ 通常叫双下iter方法
	'''
2.可迭代对象的范围
	不可迭代对象
		int float bool 函数对象
   不可迭代对象
		str list dict tuple set 文件对象
3.可迭代的含义
	'''
	迭代:更新换代(每次更新都必须依赖上一次的结果),如手机app更新
	'''
	可迭代在python中可以理解为是否支持for循环
迭代器对象
1.迭代器对象
	是由可迭代对象调用__iter__方法产生的
	迭代器对象判断的本质是看是否内置有__iter__和__next__
2.迭代器对象的作用
	提供了一种不依赖于索引取值的方式
	正因为有迭代器的存在 我们的字典 集合才能够被for循环
3.迭代器对象实操
	s1 = 'hello'   # 字符串是可迭代对象
	res = s1.__iter__()  # 将s1转换成迭代器对象 并赋值给res
	print(res.__next__())  # 迭代取值 相当于for循环的本质
	当__next__取不到值的时候 会直接报错
4.注意事项
	可迭代对象调用__iter__会成为迭代器对象 如果成为迭代器对象后还调用__iter__不会有任何的变化 还是迭代器对象本身
	s1 = 'hello'
	res = s1.__iter__().__iter__()  
   # 没有任何作用和调用一次__iter__一样
	count = 0
	while count < 5:
		print(res.__next__())
		count += 1
for循环的本质
for 变量名 i in 可迭代对象:
    循环体代码
'''
1.先将in后面的数据调用__iter__转变成迭代器对象
2.依次让迭代器对象调用__next__取值
3.一旦__next__取不到值报错 for循环会自动捕获异常并处理
'''
异常捕获/处理
1.异常
	异常就是代码运行报错 行业俗语叫bug 代码运行过程中一旦遇到异常会直接结束整个程序的运行 在编写代码的过程中要尽可能的避免
2.异常分类
	语法错误
		不允许出现 一旦出现立刻改正 否则提桶跑路
	逻辑错误
    	允许出现的 因为它一眼发现不了 代码运行之后才可能会出现
3.异常结构
	错误位置  告诉你哪行代码出现问题
	错误类型  什么错误类型 
	错误详情  为什么错误

 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号