【周报】函数进阶

上周回顾

数据类型内置方法·下

列表 list

# 索引相关
l1 = [1, 2, 3, 4]
print(l1[0])  # 1
print(l1[0:3])  # [1, 2, 3]
print(l1[3:0:-1])  # [4, 3, 2]
# 增加数据
# append()  在末尾追加单个数据
l1 = [1, 2, 3, 4, 5]
l1.append(77)
print(l1)  # [1, 2, 3, 4,]
# insert()  在任意位置插入一个数据
l1 = [1, 2, 3, 4, 5]
li.insert(3, 'jason')
print(l1)  # [1, 2, 3, 'jason', 4, 5]
# extent()  # 在末尾追加一个列表
l1 = [1, 2, 3, 4, 5]
l2 = ['jason', 'tony', 'jerry']
l1.extent(l2)
print(l1)  # [1, 2, 3, 4, 5, 'jason', 'tony', 'jerry']
# 删除数据
# del 直接删除内存空间
l1 = [1, 2, 3, 4]
del l1[2]
print(l1)  # [1, 2, 4]
del l1
print(l1)  # 报错 已不存在
# remove() 指定删除数据
l1 = ['jason', 'jerry', 'tony']
l1.remove('jerry')
print(l1)  # ['jason', 'tony']
# pop()  弹出指定数据 并可用变量名接收返回值
l1 = ['jason', 'jerry', 'tony']
res = l1.pop(2)
print(l1, res)  # ['jason', 'jerry'] tony
# index()  查询目标数据在列表的索引值
l1 = [1, 2, 3, 4, 5, 6]
res = l1.index(4)
print(res)  # 3
# count()  计算目标数据在列表的出现次数
l1 = [1, 2, 2, 3, 3, 3, 3, 4, 5]
res = l1.count(3)
print(res)  # 4
# sort()  给列表进行排序 默认升序
l1 = [2, 4, 1, 5, 3, 6, 8, 7]
l1.sort()
print(l1)  # [1, 2, 3, 4, 5, 6, 7, 8]
l1.sort(reverse=True)
print(l1)  # [8, 7, 6, 5, 4, 3, 2, 1]
# reverse()  反转列表
l1 = [2, 4, 1, 5, 3, 6, 8, 7]
l1.reverse()
print(l1)  # [7, 8, 6, 3, 5, 1, 4, 2]

字典 dict

# 增加数据与修改数据
d1 = {'name': 'jason', 'age': 18}
d1['age'] = 20  # 键存在 修改数据
print(d1)  # {'name': 'jason', 'age': 20}
d1['hobby'] = 'read'  # 键不存在 添加数据
print(d1)  # {'name': 'jason', 'age': 20, 'hobby': 'read'}
d1.update({'age': 17})  # 键存在 修改数据
print(d1)  # {'name': 'jason', 'age': 17, 'hobby': 'read'}
d1.update({'city': 'shanghai'})  # 键不存在 增加数据
print(d1)  # {'name': 'jason', 'age': 17, 'hobby': 'read', 'city': 'shanghai'}
# 删除数据
# del 通用删除
d1 = {'name': 'jason', 'age': 18}
def d1['name']
print(d1)  # {'age': 18}
# pop()  弹出数据并获取对应键的值
d1 = {'name': 'jason', 'age': 18}
res = d1.pop('age')
print(d1, res)  # {'name': 'jason'} 18
# popitem()  随机弹出数据并后去对应键的值
# 快速获取键 值 键值对
# keys()  快速获取键
d1 = {'name': 'jason', 'age': 18}
k_list = d1.keys()
print(k_list)  # dict_keys(['name', 'age'])
print(list(k_list))  # ['name', 'age']
# values()  快速获取值
d1 = {'name': 'jason', 'age': 18}
v_list = d1.values()
print(v_list)  # dict_values(['jason', 18])
print(list(v_list))  # ['jason', 18]
# items()  快速获取键值对
d1 = {'name': 'jason', 'age': 18}
kv_list = d1.items()
print(kv_list)  # dict_items([('name', 'jason'), ('age', 18)])
print(list(kv_list))  # [('name', 'jason'), ('age', 18)]
# settledefault()
dict = {'username': 'jason', 'age': 18}
res = dict.setdefault('username', 'jasonNB')
print(dict, res)  # {'username': 'jason', 'age': 18} jason
res = dict.setdefault('hobby', 'read')
print(dict, res)  # {'username': 'jason', 'age': 18, 'hobby': 'read'} read
# 键存在则获取键对应的值 键不存在则设置 并返回设置的新值

列表

# 去重
s1 = {1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2}
print(s1)  # {1, 2}
# 关系运算
s1 = {'1', '2', '4', '5'}
s2 = {'1', '3', '4', '6'}
# 取相同
print(s1 & s2)  # {'4', '1'}
# 取单个相异
print(s1 - s2)  # {'5', '2'}
# 取全部
print(s1 | s2)  # {'6', '5', '2', '1', '4', '3'}
# 取全部相异
print(s1 ^ s2)  # {'6', '5', '2', '3'}

垃圾回收机制

什么是垃圾

在内存空间中没有被使用/被绑定的无效数据

引用计数

数据每次被引用都会被标记一次
当数据身上的标记为0时,说明数据没有被引用,是垃圾数据,会被清理
若有标记,则不会被清理
但会出现循环引用的情况,极其占用空间,且是无效数据

标记清除

用于清除循环引用的情况,对全局进行扫描,标记出循环引用的数据,并清除
缺点:全盘搜索 占用资源

分代回收

根据数据的使用次数和使用频率,将数据分为0 1 2三代
使用频率越高,使用次数越高,分配的代数越高
新增加的数据都为0代,清理机制会每隔几分钟对其进行一次扫描
扫描后存活的数据会进入1代,清理机制会每隔十几分钟对其进行一次扫描
而使用频率很高或使用次数很高的数据会进入2代,会经历很长一段时间才会扫描一次

字符编码

字符编码简介

只有文本文件才有字符编码的概念 因为文本文件操作是以字符为单位
计算机内部只识别0 和1 这两个二进制机器语言
所以人为了能够简便的与机器进行交互,发明了编码,而编码会根据统一的编码表进行机器语言与人类字符的交换

字符编码发展史

一家独大 ASCII码 -> 群雄割据 各个国家有了自己的编码 如 中国:GBK -> 大一统 万国码:unicode 基于unicode出现了改良版 uft家族 主要是utf8

字符编码实操

s = '你好jason'
# encode()  编码
d = s.encode('utf8')
print(d)  # b'\xe4\xbd\xa0\xe5\xa5\xbdjason'
# decode()  # 解码
print(d.decode('utf8'))  # 你好jason

文件操控

文件操作本质

使用代码自动操作文本

文本操作语法

# a.txt文件内容如下
# 哈哈哈哈哈哈哈
# open(文件路径, 读写与操作模式, 编码)
f = open('a.txt', 'r', encoding='utf8')
print(f.read())  # 哈哈哈哈哈哈哈
f.close()  # 结束数据流 这种方法一定要添加这个语句 不然会占用资源

# with open(文件路径, 读写与操作模式, 编码) as 变量名:
# 推荐使用
with open('a.txt', 'r', encoding='utf8') as f:
    print(f.read())  # 哈哈哈哈哈哈哈
# 这个方法不需要f.close()  会自动执行该语句

# 补充
# with 可以同时打开多个文件
with open('a.txt', 'r', encoding='utf8') as f1, open('b.txt', 'r', encoding='utf8') as f2:
    pass

读写模式

# a.txt文件内容如下
# 哈哈哈哈哈哈哈
# 只读模式 r 默认模式
with open('a.txt', 'r', encoding='utf8') as f:
    print(f.read())  # 哈哈哈哈哈哈哈
# 若文件路径不存在 则会报错
# 可以使用read() 读取文件的内容

# 只写模式 w
with open('a.txt', 'w', encoding='utf8') as f:
    f.write('嘿嘿嘿嘿嘿嘿嘿')

with open('a.txt', 'r', encoding='utf8') as f:
    print(f.read())  # 嘿嘿嘿嘿嘿嘿嘿

# 若文件路径不存在 会自动创建该路径的文件
# 若文件路径存在 在打开文件前 会清空文件内容 然后才可以进行write()操作 所以使用该模式要小心
# 该模式下不可使用read()

# 只添加模式 a
with open('a.txt', 'a', encoding='utf8') as f:
    f.write('嘿嘿嘿嘿嘿嘿嘿')

with open('a.txt', 'r', encoding='utf8') as f:
    print(f.read())  # 嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿嘿

# 若文件路径不存在 会自动创建该路径的文件
# 若文件路径存在 在打开文件前 不会清空文件内容
# 进行write()操作时 默认从文件内容末尾进行追加
# 该模式下不可使用read()

操作模式

# 文本模式 默认模式
# 在默认情况下 r w a 其实是 rt wt at
# 文本模式下
# 只能对文本文件进行操作
# 操作以字符为单位
# 必须定义encoding参数
# 二进制模式
# rb wb ab
# 二进制模式下
# 可以对任意类型文件进行操作
# 操作以字节为单位
# 不需要定义encoding参数

文件的多钟操作方法

read()  # 读取全部内容 读完光标位于内容末尾
readline()  # 只读一行内容 读完光标位于第二行开头
readlines()  # 读取全部内容 将每行内容以列表的形式返回 读完后光标位于内容末尾
readable()  # 判断文件是否可读 返回一个布尔值
write()  # 写入内容
writelines()  # 将列表数据依次 以一行行的形式写入文件
writable()  # 判断文件是否可写 返回一个布尔值
flush()  # 类似 ctrl + s 保存文件

文件内光标的移动

# read() 括号内可以填写数字
# 在文本模式下
# 数字表示每次读取的字符数
# 在二进制模式下
# 数字表示每次读取的字节数数
# a.txt 内容如下
# 哈哈ha哈哈haha
with open('a.txt', 'r', encoding='utf8') as f:
    print(f.read(3))  # 哈哈h
    print(f.read(4))  # a哈哈h  从第四个字符开始读取
with open('a.txt', 'rb') as f:
    print(f.read(3).decode('utf8'))  # 哈  因为中文字符在utf8中占三个字节 所以3个字节只读取到了1个中文字符
    print(f.read(4).decode('utf8'))  # 哈h  从第四个字节开始读取
# tell() 可以记录当前光标的位置 既与内容首部的距离
# a.txt 内容如下
# 哈哈ha哈哈haha
with open('a.txt', 'r', encoding='utf8') as f:
    print(f.read(3))  # 哈哈h
    print(f.tell())  # 7
    print(f.read(4))  # a哈哈h  从第四个字符开始读取
    print(f.tell())  # 15
# tell() 在任何模式下 都是以字节为单位进行记录的
with open('a.txt', 'rb') as f:
    print(f.read(3).decode('utf8'))  # 哈
    print(f.tell())  # 7
    print(f.read(4).decode('utf8'))  # 哈h
    print(f.tell())  # 15
# seek() 可以指定光标移动的距离 以及移动的模式
# seek() 有两个参数 offset whence
# offset 表示移动的字节数
# whence 表示移动的模式
# 0 表示从内容首部开始移动 可以在文本模式下使用 默认模式
# 1 表示从当前光标开始移动 不可以在文本模式下使用
# 2 表示从文件末尾开始移动 不可以在文本模式下使用
# 文件a.txt的内容如下
# 你好a你好a你a好a
with open('a.txt', 'r', encoding='utf8') as f:
    f.read(4)
    print(f.tell())  # 10
    f.seek(0)  #  将光标指向0,第二个数值不写为0
    print(f.tell())  # 0
    f.seek(6)
    print(f.tell())  # 6
    print(f.read())  # a你好a你a好a啊  seek移动的位移量也以字节为单位

with open('a.txt', 'rb') as f:
    f.seek(-3, 2)
    print(f.tell())  # 22
    f.seek(-3, 1)
    print(f.tell())  # 19
    f.seek(10, 1)
    print(f.tell())  # 29
    print(f.read())  # b''  超出内容长度 无法读取内容
    f.seek(-3, 0)  # 报错 因为开头不能再往前了

文件修改两种方法

# 覆盖写
with open('a.txt', 'rb') as f:
    data = f.read()

with open('a.txt'm 'wb') as f:
    f.write(data)
"""
这种方法只占用一个磁盘空间,但操作大文件时会造成内存溢出
"""

# 重命名
with open('a.txt', 'rb') as f1, open('b.txt', 'wb') as f2:
    for line in f1:  # 通过for循环一次获取每一行的数据
        f2.write(line)
# 然后删除a.txt 给b.txt重命名为a.txt
# 这种方法同一时间可能占用两个磁盘空间,但大幅度减少了内存溢出的概率

函数

函数的简介

用于减少重复代码的编译量

函数与循环的关系

循环是在相同的位置反复执行相同的代码
函数是在不同的位置反复执行相同的代码

函数的本质

可以将函数看成制作完成(定义)的工具 在需要使用(调用)时 直接使用(调用)

函数的语法结构

# 定义
def 函数名(形参1, 形参2, ...):
    """注释"""
    函数体代码
    return 返回值
# 调用
函数名(实参1, 实参2, ...)

语法结构分析

在定义阶段
def 定义函数的关键字
函数名 用于绑定函数体代码 定义时尽量做到见名知意

def get_user_info():  # 可以知道 该函数用于 获取用户信息

括号 在定义函数时函数名后面必须跟括号
形参 函数体需要输入的数据 一般来讲 有多少形参 调用函数时就需要输入多少数据
注释 用于介绍函数的功能 以及需要的参数
函数体代码 整个函数的核心
return 用于返回数据的关键字 可以不写 默认为None
返回值 使调用函数时产生一个可以被接收的数据 接收方式 变量名 = 函数名()

在调用阶段
函数名 使用函数名加括号的形式进行调用
括号 函数调用时必须添加括号
实参 与形参绑定 一般情况下 有多少形参 就需要输入多少实参

函数的定义与调用

定义与调用的关系
函数在调用前 必须先定义 既定义在调用语句的上面

定义与调用的方法
通过def关键字进行定义,使用函数名加括号的方式进行调用

定义与调用逻辑
函数在定义阶段不会执行代码块代码,在调用时才会执行,在定义阶段只检测函数体代码语法

函数名解释
函数名绑定的是一块内存地址,里面存放了函数体代码
要想运行该代码 就需要调用函数>>>:函数名加括号
函数名加括号执行优先级最高(定义阶段除外)

函数的分类

内置函数
解释器提前定义好的函数,用户可以直接调用,如len() print() input()
内置函数可以直接调用,但数据类型的内置方法必须以数据类型.内置方法的方式才可以使用
既数据类型的独有方法

l1 = [1, 2, 3, 4, 5]
print(len(l1))  # 5 print 和 len 可以直接使用
l1.remove(2)  # 需要以 数据类型.内置方法 的方式
print(l1)  # [1, 3, 4, 5]

空函数
没有实际的意义,用于前期框架的搭建,表明要实现的功能

def run():
    pass

def fly():
    pass

# 知道在run之后再执行fly
run()
fly()

无参函数
函数定义阶段没有形参

def func():  # 没有形参
    print('这里是func')

func()  # 不需要参数就可以调用
# 执行结果
# 这里是func

有参函数
函数定义阶段有形参

def func(a, b):  # 有参数 需要输入
    print(a, b)

func(2, 3)  # 需要输入实参
# 执行结果
# 2 3

函数的返回值

返回值的性质
返回值就是调用函数之后产生的结果,有时可有可无
通过以下方法获取函数返回值
变量名 = 函数名()
上述方法有返回值则返回返回值,没有则默认返回 None

情况1:函数无return关键字
返回None

def func():pass  # 函数内若只有一行 可以写成这种形式
res = func()
print(res)  # None

情况2:函数有return关键字 但return后不跟数据
返回None

def func(): return
res = func()
return(res)  # None

情况3:函数有return关键字 return后跟单个数据
返回该数据

def func(): return 123
res = func()
print(res)  # 123

情况4:函数有return关键字 return后跟多个数据
将多个数据以元组的形式传出

def func(): return 123, 342, 221, 2233
res = func()
print(res)  # (123, 342, 221, 2233)

return 在函数中的作用
就像break在循环中的作用 读到就会退出函数

def func():
    print(123)
    return
    print(321)

func()
# 运行结果
# 123

函数的参数

参数的分类
参数可大致分为 形参和实参两种参数
形参是函数定义阶段括号内的参数
实参是函数调用阶段括号内的参数

def func(形参):
    pass
func(实参)

形参与实参的关系
形参与实参就像是变量名与数据值
函数调用的阶段 实参与形参 临时绑定 在函数执行结束时结束绑定关系

位置参数
在函数定义阶段括号内从左往右依次填写的变量名,称之为位置形参
在函数调用阶段括号内从左往右依次填写的数据值,称之为位置实参

关键字参数
在函数调用阶段可以以 形参=数据值 的形式 为形参赋值 关键字参数必须在位置参数后面
多个关键字参数之间可以随意调动位置

def func(a, b, c):
    print(a, b, c)
func(123, c=222, b=999)
# 运行结果
# 123 999 222
"""
1.指名道姓的给形参传值(打破了位置的限制)
2.位置实参必须在关键字实参的前面
    小诀窍:无论是形参还是实参 都遵循短(简单)的在前面 长(复杂的)的在后面
3.同一个形参在一次调用中 只能传一次值
"""

默认值参数
在函数定义阶段可以给形参指定一个默认值
在函数调用阶段就可以不输入该形参的数据 而使用默认数据
默认值参数必须在位置参数的后面

def func(a, b, c=888):
    print(a, b, c)
func(b=999, a=777)
# 运行结果
# 777 999 888

可变长参数
可以打破形参与实参的个数限制,随意传值

def func(*args, **kwargs):  # args 和 kwargs 只是变量名 可以任意修改 但要符合命名规范
    print(args, kwargs)

func(1, 2, 3, 4, 5, 6, 7, name='jason', age=18)
# 运行结果
# (1, 2, 3, 4, 5, 6, 7) {'name': 'jason', 'age': 18}

本周内容

内容概要

  • 函数进阶
    • *和 ** 在实参中的作用
    • 命名关键字参数
    • 名称空间
    • 名称查找顺序
    • 名称空间的作用域
    • global与nonlocal关键字
    • 函数名的多钟使用方式
  • 装饰器
    • 闭包函数
    • 装饰器简介
    • 装饰器推导
    • 装饰器固定模板
    • 装饰器语法糖
    • 装饰器修复技术
  • 装饰器进阶
    • 多层装饰器
    • 有参装饰器
  • 小算法
    • 递归函数
    • 算法(二分法)
    • 常见算法
  • 各种偷懒方式
    • 三元表达式
    • 各种生成式
    • 匿名函数
    • 常用内置函数
    • 重要内置函数

函数进阶

* 和 ** 在实参中的作用

*可以将列表中的数据依次传入形参中

def func(*args): print(args)
l1 = [1, 2, 3, 4, 5]
func(l1)  # ([1, 2, 3, 4, 5], )  可以看出 只是将列表传入 而不是内部数据依次传入
func(*l1)  # (1, 2, 3, 4, 5)

**可以将字典中的数据已关键字参数的形式传入

def func(*args, **kwargs): print(args, kwargs)
d1 = {'name': 'jason', 'age': 18}
func(d1)  # ({'name': 'jason', 'age': 18},) {}
func(**d1)  # () {'name': 'jason', 'age': 18}

命名关键字参数

作用:要求函数的某些参数必须以关键字参数的形式传参

def func(a, b, *args, c, **kwargs):  # * 和 ** 中的形参 就必须以关键字的形式传入
    print(a, b, args, c, kwargs)

func(1, 2, 3, 4, 5, c=4, f=15, e=123)
# 运行结果
# 1 2 (3, 4, 5) 4 {'f': 15, 'e': 123}

名称空间

名称空间的定义
用于存储变量名与数据的绑定关系的空间

名称空间的分类
内置名称空间
python解释器打开时立刻创建的空间
用于存放内置方法 与内置变量
无法直接修改或删除该空间的东西
解释器关闭 空间关闭

全局名称空间
py文件运行是产生的名称空间 用于存储程序运行后产生的变量名
程序结束 空间关闭

局部名称空间
函数调用是产生的名称空间 用于存储函数调用阶段产生的变量名
函数运行结束 空间关闭

名称查找顺序

在查找名称空间时 要先知道当前在什么空间
1.若在局部名称空间
查找顺序:局部名称空间>>>全局名称空间>>>内置名称空间
2.若在全局名称空间
查找顺序:全局名称空间>>>内置名称空间

名称空间的作用域

内置名称空间
在全局任意位置都可以调用(全局有效)

全局名称空间
在全局任意位置都可以调用(全局有效)

局部名称空间
在各自局部空间可以调用(局部有效)

global 与 nonlocal关键字

主要用于修改不可变类型的数据

# global 用于在局部空间修改全局名词空间中的数据
a = 10
def func1():a = 20
func1()
print(a)  # 10  只是在局部空间内产生了一个新的a 并没有修改全局名称空间中的a

def func2():
    global a
    a = 20
func2()
print(a)  # 20  修改成功

# 若想要修改的数据为可变类型
l1 = [1, 2, 3, 4, 5]
def func():l1.append(6)
func()
print(l1)  # [1, 2, 3, 4, 5, 6]  也可以修改 因为可变类型的操作只是修改自身数据 并没有产生新的值
# nonlocal 用于在嵌套局部空间中 修改上一层局部空间内的数据
def func1():
    a = 10
    def func2():
        a = 20
    func2()
    print(a)

func1()  # 10  只是在内部空间的内部空间中产生了一个新的a

def func3():
    a = 10
    def func4():
        nonlocal a
        a = 20
    func4()
    print(a)

func3()  # 20

函数名的多钟使用方式

函数名可以用作赋值

def func():
    print('from func')

res = func  # 将 func 所绑定的代码块 与 res 进行绑定
res()
# 运行结果
# from func

函数名可以用作返回值

def func1():
    def func2():
        print('from func2')
    return func2

res = func1()  # res 获取func1的返回值 func2 与func2所绑定的代码块进行绑定
res()  # 实则调用的是func2

# 运行结果
# from func2

函数名可以用作实参

def func1():
    print('from func1')

def func2(func_name):
    func_name()

func2(func1)

# 运行结果
# from func1

函数名可以用作容器类型数据的数据值

def func1():
    print('from func1')

l1 = [1, 2, 3, 4, func1]
l1[4]()

# 运行结果
# from func1

装饰器

闭包函数

闭包函数的条件
1.定义在函数内部的函数
2.内部函数使用的外部名称空间中的变量名

def func1():
    s = 'from func1'
    def func2():
        print(s)
    return fucn2

res = func1()
res()

# 运行结果
# from func1

装饰器简介

装饰器并不是全新的知识 而是所学的一些知识的集合

装饰器本质
在不改变被装饰函数的调用方式和内部代码的情况下给被装饰对象添加功能

装饰器原则
对修改封闭 对扩张开放

知识储备 time模块

import time  # import关键字用于导入已有的模块  模块就类似工具箱
time.time()  # 获取当前时间距离1970年1月1日0时0分0秒的 秒数 既:时间戳
# 实际运用:一段代码执行的秒数
def func():
    time.sleep(3)  # 运行到这时原地等待3秒

start_time = time.time()  # 记录开始时间
func()
end_time = time.time()  # 记录结束时间
print(end_time - start_time)  # 结束时间减去开始时间获得的秒数 就是代码执行的时间

装饰器推导

import time
def index():
    pass
start_time = time.time()
index()
end_time = time.time()
print(end_time - start_time)
# 缺陷:若要在多处运行 则代码冗余
# 解决方法
# 封装为函数
import time
def index():
    pass
def func():
    start_time = time.time()
    index()
    end_time = time.time()
    print(end_time - start_time)

func()
# 缺陷:只能给单个函数使用
# 解决方法
# 将函数名作为形参传入
import time
def index():pass
def home():pass
def func(func_name):
    start_time = time.time()
    func_name()
    end_time = time.time()
    print(end_time - start_time)

func(index)
func(home)
# 缺陷:改变了函数的调用方式
# 解决方法
# 改为闭包函数 返回函数名
import time
def index():pass
def home():pass
def outer(func_name):
    def inner():
        start_time = time.time()
        func_name()
        end_time = time.time()
        print(end_time - start_time)
    return inner

index = outer(index)
index()
home = outer(home)
home()

# 缺陷:若函数需要参数 则无法传入
# 解决方法
# 给inner添加可变形参
import time
def index(a):return a
def home(a, b):return a, b
def outer(func_name):
    def inner(*args, **kwargs):
        start_time = time.time()
        func_name(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
    return inner

index = outer(index)
res1 = index(1)
home = outer(home)
res2 = home(1, 2)
print(res1)  # None
print(res2)  # None

# 缺陷:没有返回值
# 解决方法
# 给inner添加返回值
import time
def index(a):return a
def home(a, b):return a, b
def outer(func_name):
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func_name(*args, **kwargs)
        end_time = time.time()
        print(end_time - start_time)
        return res
    return inner

index = outer(index)
res1 = index(1)
home = outer(home)
res2 = home(1, 2)

print(res1)  # 1
print(res2)  # (1, 2)

装饰器模板

def outer(func_name):
    def inner(*args, **kwargs):
        """这里执行函数运行前执行的代码"""
        res = func_name(*args, **kwargs)
        """这里执行函数运行后执行的代码"""
        return res
    return inner

装饰器语法糖

def outer(func_name):
    def inner(*args, **kwargs):
        """这里执行函数运行前执行的代码"""
        res = func_name(*args, **kwargs)
        """这里执行函数运行后执行的代码"""
        return res
    return inner

@outer  # 可以看做 func = outer(func)
def func():
    pass

装饰器修复技术

就是让装饰器隐藏的更好 目前没觉得有用

from functools impoer wraps
def outer(func_name):
    @wraps(func_name)
    def inner(*args, **kwargs):
        """这里执行函数运行前执行的代码"""
        res = func_name(*args, **kwargs)
        """这里执行函数运行后执行的代码"""
        return res
    return inner

@outer  # 可以看做 func = outer(func)
def func():
    pass

装饰器进阶

多层装饰器

def outer1(func_name):
    print('加载了 outer1')
    def inner(*args, **kwargs):
        print('from outer1')
        res = func_name(*args, **kwargs)
        return res
    return inner

def outer2(func_name):
    print('加载了 outer2')
    def inner(*args, **kwargs):
        print('from outer2')
        res = func_name(*args, **kwargs)
        return res
    return inner

def outer3(func_name):
    print('加载了 outer3')
    def inner(*args, **kwargs):
        print('from outer3')
        res = func_name(*args, **kwargs)
        return res
    return inner


@outer1  # 看做 outer2 = outer1(outer2)
@outer2  # 看做 outer3 = outer2(outer3)
@outer3  # 看做 func = outer3(func)
def func():
    pass

# 运行结果
# 加载了 outer3
# 加载了 outer2
# 加载了 outer1
# 得知 绑定装饰器时 从下往上开始加载

# 加入func()
func()

# 运行结果
# 加载了 outer3
# 加载了 outer2
# 加载了 outer1
# from outer1
# from outer2
# from outer3
# 得知 运行函数时 装饰器从上往下运行

有参装饰器

作用:用于对数据来源的判定 分组

# 当要对装饰器内输入参数时 发现
def outer(func_name):  # 这里不能输入 语法糖加入时无法输入参数
    def inner(*args, **kwargs):  # 这里不能输入 因为若要添加一个额外的参数 函数调用时修改了调用方式
        res = func_name(*args, **kwargs)
        return res
    return inner

# 解决方法
# 对装饰器整体进行闭包
def outer1(target_user):  # 在这就可以输入参数并且不影响原函数的调用方式 与内部代码
    def outer(func_name):
        def inner(*args, **kwargs):
            if target_user == 'student':
                print('调用了有关学生的方法')
            elif target_user == 'teacher':
                print('调用了有关老师的方法')
            res = func_name(*args, **kwargs)
            return res
        return inner
    return outer

# 使用方法
@outer1('student')
def get_student_info():pass
@outer1('teacher')
def get_teacher_info():pass

get_student_info()
get_teacher_info()
get_teacher_info()
get_student_info()

# 运行结果
# 调用了有关学生的方法
# 调用了有关老师的方法
# 调用了有关老师的方法
# 调用了有关学生的方法

小算法

递归函数

递归函数定义
一个函数间接或直接的调用了自己

# 直接调用
def index():
    ...
    index()
    ....

# 间接调用
def index():
    ...
    home()
    ...

def home():
    ...
    index()
    ...

递归最大限制
在python中递归有限制 默认为1000 但有误差 可能996 995 997 998

递归应用场景
递推:一层层往下寻找答案
回溯:根据已知条件推导最终结果

递归要求
每次调用的时候都必须要比上一次简单!!!
并且递归函数最终都必须要有一个明确的结束条件!!!

算法(二分法)

算法的定义
解决问题的方法

二分法
局限性:数据有序排序,当数据在开头或结尾时,比顺序查找费时间

def get_num(num_list, left, right, target_num):
    if left > right:
        print('没找到')
        return
    mid_index = left + (right - left) // 2
    mid_value = num_list[mid_index]
    if mid_value < target_num:
        get_num(num_list, mid_index + 1, right, target_num)
    elif mid_value > target_num:
        get_num(num_list, left, mid_index - 1, target_num)
    else:
        print(f'找到了 在索引{mid_index}')

常见算法

冒泡
快排
插入
堆排
桶排

各种偷懒方式

三元表达式

三元表达式作用
减少代码量

三元表达式语法

# 语法
# 数据值1 if 条件 else 数据值2
# 条件成立 返回数据1  条件不成立 返回数据2

# 使用
res = 10 if 5 > 20 else 12
print(res)  # 12

ps:在python中 代码不是精简的越少越好 而是在精简的过程中保障代码的可读性

各种生成式

列表生成式

# 例子
l1 = [1, 2, 3, 4, 5, 6]
l2 = [i for i in l1 if i != 4]
print(l2)  # [1, 2, 3, 5, 6]

# 语法结构
# [变量名的相关处理 for 变量名 in 数据集]
# 先执行for循环  然后将一个个的数据值交给for循环前面处理
# [变量名的相关处理 for 变量名 in 数据集 if 条件]
# 先执行for循环  然后将一个个的数据值交给if判断 结果为True则最后交给for循环前面处理

字典生成式

# 例子
new_dict = {i: 'jason' for i in range(4)}
print(new_dict)  # {0: 'jason', 1: 'jason', 2: 'jason', 3: 'jason'}
new_dict = {i: 'jason' for i in range(4) if i != 2}
print(new_dict)  # {0: 'jason', 1: 'jason', 3: 'jason'}

# 语法结构
# [K: V for 变量名 in 数据集]
# 可以在外部定义V 或 K 然后在内部进行输入
# [K: V for 变量名 in 数据集 if 条件]
# 与列表生成式相似

集合生成式

# 与上面两个相似

匿名函数

匿名函数定义
没有名字的函数

关键字
lambda

语法结构

lambda 形参:返回值

# 可以看做

def xxx(形参):  # xxx只是随便写的 不是说匿名函数的函数名为xxx
    return 返回值

匿名函数作用
常用作与各种内置函数结合使用

常用内置函数

求最大值 max()

# 源码分析
# def max(*args, key=None):
"""
可以看到max输入的参数最终会变成一个元组 最后会对元组内的数据集进行判断 取出最大值
key是需要输入的函数名
一般搭配匿名函数使用
执行原理为 对第一个可变长参数做类似for循环处理 每次将其中的数传给后面的函数做判断
"""

# 例子1
l1 = [12, 123124, 12412312, 1412512512, 1231, 14124, 12315]
print(max(l1))  # 1412512512

# 例子2
dic = {
    'jason': 100,
    'kevin': 200,
    'Baby': 500,
    'zoy': 20
}
print(max(dic))  # zoy 为什么是zoy呢 明明数据只有20
"""
还记得吗 字典参与for循环只能获取K 所以这里也是这样
只是对名字进行了排序
而字符串之间比大小比的是ASCII码
A-Z为 65-90
a-z为 97-122
"""
# 那怎么对字典的值进行判断呢 可以用到key这个参数
res = max(dic, key=lambda k: dic.get(k))
print(res)  # Baby

求最小值min()
与max相似

重要内置函数

map()--映射

l1 = [1, 2, 3, 4, 5, 6]
# 需求:将列表内所有数据自增10
res = map(lambda x: x+10, l1)
print(res)  # <map object at 0x00000151CE92A0A0>
print(list(res))  # [11, 12, 13, 14, 15, 16]

filter()--过滤

l1 = [1, 2, 3, 4, 5, 6]
# 需求:将4移除
res = filter(lambda x: x!=4, l1)
print(res)  # <filter object at 0x000002275135A100>
print(list(res))  # [1, 2, 3, 5, 6]

reduce()--求和

l1 = [1, 2, 3, 4, 5, 6]
# 需求:求全部数据的和
from functools import reduce
res = reduce(lambda x, y: x + y, l1)
print(res)  # 21

zip()--链条

n1 = [1,2,3]
n2 = ['jason','kevin','oscar']
res = zip(n1,n2)
print(list(res))  # [(1, 'jason'), (2, 'kevin'), (3, 'oscar')]
'''
1.可以拼接多个值
2.拼接的值要支持for循环
3.如果存放的数据长短不一,那就按照最短的那个来连接
'''

posted on   祁珏  阅读(45)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

点击右上角即可分享
微信分享提示