函数
* 和 ** 分别在形参和实参中的用法
- 形参
- *参数:函数接收任意长度的非关键字参数,系统将这些参数用tuple元组组合起来表示
- **参数:函数接收任意长度的关键字参数,系统将这些参数用dict字典组合起来表示
- 混合使用:关键字必须在位置参数后面,否则报错
- 实参
- *参数:
- 实参中的 * 后面跟的是可迭代对象,获取的是可迭代对象的 key,此时形参为位置参数或可变参数都可以
- 除了list, str, tuple等,还可以用dict作为实参,获取的是dict中的key值
- ** 参数:实参中的 ** 后面跟的是可迭代映射,获取的是可迭代映射的 value。此时,当形参为位置参数时,获取的是可迭代映射的value,当形参为关键字参数时,获取的是dict
命名关键字参数(了解)
- 需要形参传实参的时候必须按照关键字
- 在形参 * args的后面
- 如果形参中还有** kwargs,那必须在它的前面
名称空间
- 定义:用来存放变量名与数据值之间绑定关系的地方
- 分类:
- 内置名称空间:python解释器运行就会立刻创建的空间(代码过程中可以直接使用的名字都在该空间中)
- 全局名称空间:py文件运行代码过程中产生的名字都会存入该空间(普通代码分支结构循环结构里的变量名、定义函数的函数名、定义类的类名)
- 存活周期:
- 内置名称空间:解释器运行(创建),解释器关闭(销毁)
- 全局名称空间:py文件运行(创建),py文件结束(销毁)
- 局部名称空间:函数体代码运行(创建),函数体代码结束(销毁)
名字查找顺序
-
当前在全局名称空间:全局名称空间 → 内置名称空间
-
当前在局部名称空间:局部名称空间 → 全局名称空间 → 内置名称空间
-
名字的查找顺序默认情况下不能颠倒只能是:局部名称空间 → 全局名称空间 → 内置名称空间
名称空间的作用域
- 内置名称空间/全局名称空间:在程序任意位置都可以使用(全局有效)
- 局部名称空间:在各自的局部空间可以使用(局部有效)
局部名称空间复杂情况
- 各自局部名称空间默认情况下不能彼此共享名字
| def func1(): |
| name = 'jason' |
| print(age) |
| |
| def func2(): |
| age = 18 |
| print(name) |
| |
| func1() |
| func2() |
- 特殊情况
| x = 1 |
| def func1(): |
| x = 2 |
| def func2(): |
| x = 3 |
| def func3(): |
| print(x) |
| |
| func3() |
| func2() |
| func1() |
- 函数在定义阶段其实名字的查找顺序就已经固定死了
| name = 'jason' |
| def func(): |
| print(name) |
| name = 'jasonNB' |
| func() |
global与nonlocal关键字
- 正常情况下,局部名称空间里面出现新的名字会在局部名称空间中存储,但是有时候需要在局部名称空间中修改全局名称空间的名字
| money = 999 |
| |
| def func(): |
| money = 1000 |
| print(money) |
| func() |
| print(money) |
| |
| def func(): |
| global money |
| money = 1000 |
| print(money) |
| func() |
| print(money) |
- 局部修改全局名称空间中不可变类型的数据,需要使用关键字global声明,如果是可变类型,则无需关键字声明
| l1 = [1, 2, 3, 4, 5] |
| s = '$jason$' |
| |
| def func(): |
| s = 'jason' |
| res = s.strip('$') |
| l1.append(113123) |
| l1.append(666) |
| func() |
| print(l1) |
| |
| def func(): |
| global s |
| s = 'jason' |
| res = s.strip('$') |
| l1.append(113123) |
| l1.append(666) |
| func() |
| print(l1) |
- nonlocal 在内存局部名称空间修改外层局部名称空间中的不可变类型
| def func1(): |
| x = 1 |
| l1 = [1,2] |
| def func2(): |
| |
| x = 999 |
| l1.append(666) |
| func2() |
| print(x) |
| print(l1) |
| func1() |
| |
| def func1(): |
| x = 1 |
| l1 = [1,2] |
| def func2(): |
| nonlocal x |
| x = 999 |
| l1.append(666) |
| func2() |
| print(x) |
| print(l1) |
| func1() |
函数名的多种使用方式
闭包函数
| |
| def func(xxx): |
| print(xxx) |
| |
| def index(): |
| username = 'jason' |
| def func(): |
| print(username) |
| return func |
| res = index('jason') |
| res() |
装饰器
简介
- 概念:函数知识整合到一块的产物
- 本质:在不改变装饰对象原来的‘调用方式’和‘内部代码’的情况下给被装饰的对象添加功能
- 原则:对修改封闭,对扩展开放
装饰器固定模板
| from functools import wraps |
| def outer(func_name): |
| @wraps(func_name) |
| def inner(*args, **kwargs): |
| print('执行被装饰对象之前可以做的额外操作') |
| res = func_name(*args, **kwargs) |
| print('执行被装饰对象之后可以做的额外操作') |
| return res |
| return inner |
装饰器语法糖
| import time |
| |
| from functools import wraps |
| def outer(func_name): |
| @wraps(func_name) |
| def inner(*args, **kwargs): |
| print('执行被装饰对象之前可以做的额外操作') |
| res = func_name(*args, **kwargs) |
| print('执行被装饰对象之后可以做的额外操作') |
| return res |
| return inner |
| |
| |
| @outer |
| def home(): |
| '''我是home函数 我要热死了!!!''' |
| time.sleep(1) |
| print('from home') |
| return 'home返回值' |
| home() |
多层装饰器
- 语法糖功能:会自动将下面紧挨着的函数名当做参数传递给@符号后面的函数名(加括号调用)
- 多个语法糖装饰一个函数名:从下往上执行最后一个语法糖才会做重命名操作
| 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() |
| |
| |
| |
| |
| |
| |
| |
有参装饰器
- 有参装饰器:编写一个装饰器时,当这个装饰器内部需要外界传入额外的数据来控制代码的分支,就需要给原来的装饰器模板再套用一层,加语法糖时就直接添加额外的数据
| def outer(condition,type_user): |
| def login_auth(func_name): |
| def inner(*args, **kwargs): |
| username = input('username>>>:').strip() |
| password = input('password>>>:').strip() |
| |
| if type_user =='jason':print('VIP') |
| if condition == '列表': |
| print('使用列表作为数据来源 比对用户数据') |
| elif condition == '字典': |
| print('使用字典作为数据来源 比对用户数据') |
| elif condition == '文件': |
| print('使用文件作为数据来源 比对用户数据') |
| else: |
| print('目前只有上面几种方式') |
| return inner |
| return login_auth |
| @outer('文件','jason') |
| def index(): |
| print('from index') |
| index() |
递归函数
- 概念:在函数内部直接或间接调用自己的函数
- python中允许函数最大递归调用次数,官方给出的限制是1000,代码验证可能会有偏差(996 997 998...都有可能)
- 应用场景:
- 递推:层层往下寻找答案(每一次都是基于上一次进行下一次的执行)
- 回溯:遇到终止条件,则从最后往回返一级一级的把值返回来
- 特点:
| l1 = [1, [2, [3, [4, [5, [6, [7, [8, [9, [10,]]]]]]]]]] |
| |
| def get_num(l1): |
| for i in l1: |
| if isinstance(i, int): |
| print(i) |
| else: |
| get_num(i) |
| get_num(l1) |
算法之二分法
算法
-
定义:解决问题的方法
-
算法永远都在精进,但是很少有最完美的算法
二分法
-
定义:二分法是所有算法里最简单的算法
-
前提:数据集必须有顺序(升序/降序)
-
原理:获取数据集中间的元素,比对大小
| l1 = [11, 23, 32, 45, 65, 78, 90, 123, 432, 467, 567, 687, 765, 876, 999, 1131, 1232] |
| |
| def get_num(l1, target_num): |
| |
| if len(l1) == 0: |
| print('不好意思 找不到') |
| return |
| |
| middle_index = len(l1) // 2 |
| middle_value = l1[middle_index] |
| |
| if target_num > middle_value: |
| |
| right_l1 = l1[middle_index + 1:] |
| |
| |
| |
| |
| print(right_l1) |
| get_num(right_l1, target_num) |
| elif target_num < middle_value: |
| left_l1 = l1[:middle_index] |
| |
| |
| |
| |
| print(left_l1) |
| get_num(left_l1, target_num) |
| else: |
| print('找到了', target_num) |
| |
| |
| |
| get_num(l1, 1000) |
- 缺陷:
- 数据集必须是有序的
- 查找的数如果在开头或结尾而分发效率更低
三元表达式
- 语法结构:值1 if 条件 else 值2(注意:没有冒号)
- 如果if后面的条件成立,则使用if前面的值
- 如果if后面的条件不成立,则使用else后面的值
| |
| |
| def max_num(a, b): |
| if a > b: |
| return a |
| else: |
| return b |
| res = max_num(1, 10) |
| print(res) |
| |
| |
| def max_num(a, b): |
| return a if a > b else b |
| res = max_num(1, 10) |
| print(res) |
生成式
列表生成式
| |
| name_list = ['jason', 'kevin', 'oscar', 'jerry', 'tony'] |
| |
| new_list = [] |
| |
| for name in name_list: |
| |
| new_name = name + '_NB' |
| |
| new_list.append(new_name) |
| print(new_list) |
| |
| |
| |
| new_list = [name + '_NB' for name in name_list] |
| print(new_list) |
| |
| new_list = [name + '_NB' for name in name_list if name != 'jason'] |
| print(new_list) |
字典生成式
- 概念:通过在大括号{} 内执行一行for循环语句创建一个新的字典,大括号内的语句需要指定键值对的key与value,这两项值都是for循环语句中变量
- 语法结构:{key: values for 变量名 in 数据集} 或 {key: values for 变量名 in 数据集 if 条件}
| new_dict = {i: 'jason' for i in range(10)} |
| print(new_dict) |
| |
| new_dict = {i: 'jason' for i in range(10) if i == 6} |
| print(new_dict) |
集合生成式
- 概念:python集合生成式与列表生成式几乎是一模一样的,只需要将[]替换为{} 即可,在{}内执行一个for循环语句,for循环所遍历的元素自动添加到集合之中
- 语法结构:{表达式 for 变量 in range()} 或 {表达式 for 变量 in range() if 条件}
| new_set = {i for i in range(10)} |
| print(new_set) |
| |
| new_set = {i for i in range(10) if i == 6} |
| print(new_set) |
注意:元组没有生成式,它是后面重点讲解的迭代器知识(生成器)
匿名函数
- 概念:匿名函数,也称为lambda函数,是没有定义名称的函数
- 语法结构:lambda 形参:返回值(lambda函数可以具有任意数量的参数,但只能有一个表达式,不用写return,返回值就是该表达式的结果。Lambda函数可在需要函数对象的任何地方使用)
- 注意:lambda 表达式创建的函数只能包含一条表达式
- 案例
| (lambda x: x + 1)(123) |
| res = lambda x: x + 1 |
| print(res(123)) |
- 应用场景:通常都需要配合其他函数一起使用,用于减少代码
重要内置函数
max函数
- max():最大值。如果值是字符串,则按字母顺序进行比较。如果值是字符串,则按字母顺序进行比较
| |
| dic = { |
| 'jason': 100, |
| 'aj': 123, |
| 'Bason': 9999999, |
| 'zero': 888 |
| } |
| def index(k): |
| return dic.get(k) |
| |
| res = max(dic, key=index) |
| print(res) |
min函数
- min():最小值。如果值是字符串,则按字母顺序进行比较。如果值是字符串,则按字母顺序进行比较
| |
| dic = { |
| 'jason': 100, |
| 'aj': 123, |
| 'Bason': 9999999, |
| 'zero': 888 |
| } |
| |
| def index(k): |
| return dic.get(k) |
| |
| res = min(dic, key=index) |
| print(res) |
map函数
| |
| l1 = [11, 22, 33, 44, 55, 66] |
| res = map(lambda x: x + 20, l1) |
| print(res) |
| print(list(res)) |
filter函数
| |
| l1 = ['jason', 'kevin', 'oscar', 'tony'] |
| res = filter(lambda a: a != 'jason', l1) |
| print(res) |
| print(list(res)) |
reduce函数
| |
| l2 = [1, 2, 3] |
| from functools import reduce |
| |
| res = reduce(lambda x, y: x + y, l2, 100) |
| res = reduce(lambda x, y: x + y, l2, 100) |
| print(res) |
zip函数
| n1 = [1, 2, 3] |
| n2 = ['jason', 'kevin', 'oscar'] |
| res = zip(n1, n2) |
| print(res) |
| print(list(res)) |
| |
| n1 = [1, 2, 3, 4] |
| n2 = [5, 6, 7, 8] |
| n3 = 'jack' |
| res = zip(n1, n2, n3) |
| print(list(res)) |
| |
| |
| n1 = [1, 2, 3, 4, 5, 6, 7] |
| n2 = [5, 6, 7, 8] |
| n3 = 'jason' |
| res = zip(n1, n2, n3) |
| print(list(res)) |
练习题
| 1.编写一个用户认证装饰器 |
| 基本要求 |
| 执行每个函数的时候必须先校验身份 eg: jason 123 |
| 拔高练习(有点难度) |
| 执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过 |
| 函数:register login transfer withdraw |
| 提示:全局变量 记录当前用户是否认证 |
| is_login = {'is_login': False, } |
| |
| def login_auth(func_name): |
| def inner(*args, **kwargs): |
| |
| if is_login.get('is_login'): |
| |
| res = func_name(*args, **kwargs) |
| return res |
| |
| username = input('username>>>:').strip() |
| password = input('password>>>:').strip() |
| if username == 'jason' and password == '123': |
| res = func_name(*args, **kwargs) |
| |
| is_login['is_login'] = True |
| return res |
| else: |
| print('权限不够 无法执行') |
| return inner |
| |
| |
| @login_auth |
| def register(): |
| print('注册功能') |
| |
| @login_auth |
| def login(): |
| print('登录功能') |
| |
| @login_auth |
| def transfer(): |
| print('转账功能') |
| |
| @login_auth |
| def withdraw(): |
| print('提现功能') |
| |
| register() |
| login() |
| transfer() |
| withdraw() |
| |
| 2.尝试编写有参函数将多种用户验证方式整合到其中 |
| 直接获取用户数据比对 |
| 数据来源于列表 |
| 数据来源于文件 |
| data_list = ['jason|123', 'kevin|321', 'oscar|222'] |
| |
| def login_auth(condition): |
| def outer(func_name): |
| def inner(*args, **kwargs): |
| username = input('username>>>:').strip() |
| password = input('password>>>:').strip() |
| if condition == 'absolute': |
| if username == 'jason' and password == '123': |
| res = func_name(*args, **kwargs) |
| return res |
| else: |
| print('你不是jason 不允许执行') |
| elif condition == 'list_type': |
| for user_data in data_list: |
| real_name, real_pwd = user_data.split('|') |
| if real_name == username and real_pwd == password: |
| res = func_name(*args, **kwargs) |
| return res |
| elif condition == 'file_type': |
| with open(r'userinfo.txt','r',encoding='utf8') as f: |
| for line in f: |
| real_name, real_pwd_n = line.split('|') |
| if real_name == username and password == real_pwd_n.strip('\n'): |
| res = func_name(*args, **kwargs) |
| return res |
| return inner |
| return outer |
| |
| @login_auth('absolute') |
| def func1(): |
| print('from func1') |
| return 1 |
| @login_auth('list_type') |
| def func2(): |
| print('from func2') |
| return 2 |
| @login_auth('file_type') |
| def func3(): |
| print('from func3') |
| return 3 |
| |
| |
| func1() |
| func2() |
| func3() |
| |
| 3.尝试编写递归函数 |
| 推导指定某个人的正确年龄 |
| eg: A B C D E 已知E是18 求A是多少 |
| def get_age(n): |
| if n == 1: |
| return 18 |
| return get_age(n-1) + 2 |
| |
| res = get_age(5) |
| print(res) |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)