随笔- 71  文章- 17  评论- 0  阅读- 5326 

函数

* 和 ** 分别在形参和实参中的用法

  • 形参
    • *参数:函数接收任意长度的非关键字参数,系统将这些参数用tuple元组组合起来表示
    • **参数:函数接收任意长度的关键字参数,系统将这些参数用dict字典组合起来表示
    • 混合使用:关键字必须在位置参数后面,否则报错
  • 实参
    • *参数:
      • 实参中的 * 后面跟的是可迭代对象,获取的是可迭代对象的 key,此时形参为位置参数或可变参数都可以
      • 除了list, str, tuple等,还可以用dict作为实参,获取的是dict中的key值
    • ** 参数:实参中的 ** 后面跟的是可迭代映射,获取的是可迭代映射的 value。此时,当形参为位置参数时,获取的是可迭代映射的value,当形参为关键字参数时,获取的是dict

命名关键字参数(了解)

  • 需要形参传实参的时候必须按照关键字
    • 在形参 * args的后面
    • 如果形参中还有** kwargs,那必须在它的前面

名称空间

  • 定义:用来存放变量名与数据值之间绑定关系的地方
  • 分类:
    • 内置名称空间:python解释器运行就会立刻创建的空间(代码过程中可以直接使用的名字都在该空间中)
    • 全局名称空间:py文件运行代码过程中产生的名字都会存入该空间(普通代码分支结构循环结构里的变量名、定义函数的函数名、定义类的类名)
  • 存活周期:
    • 内置名称空间:解释器运行(创建),解释器关闭(销毁)
    • 全局名称空间:py文件运行(创建),py文件结束(销毁)
    • 局部名称空间:函数体代码运行(创建),函数体代码结束(销毁)

名字查找顺序

  • 当前在全局名称空间:全局名称空间 → 内置名称空间

  • 当前在局部名称空间:局部名称空间 → 全局名称空间 → 内置名称空间

  • 名字的查找顺序默认情况下不能颠倒只能是:局部名称空间 → 全局名称空间 → 内置名称空间

名称空间的作用域

  • 内置名称空间/全局名称空间:在程序任意位置都可以使用(全局有效)
  • 局部名称空间:在各自的局部空间可以使用(局部有效)

局部名称空间复杂情况

  1. 各自局部名称空间默认情况下不能彼此共享名字
def func1():
name = 'jason'
print(age)
def func2():
age = 18
print(name)
func1()
func2()
  1. 特殊情况
x = 1
def func1():
x = 2
def func2():
x = 3
def func3():
print(x) # 特例
# x = 4
func3()
func2()
func1()
  1. 函数在定义阶段其实名字的查找顺序就已经固定死了
name = 'jason'
def func():
print(name) # 报错
name = 'jasonNB'
func()

global与nonlocal关键字

  • 正常情况下,局部名称空间里面出现新的名字会在局部名称空间中存储,但是有时候需要在局部名称空间中修改全局名称空间的名字
money = 999
def func():
money = 1000
print(money) # 1000
func()
print(money) # 999
def func():
global money # 声明 局部名称空间中的money操作的是全局的money
money = 1000
print(money) # 1000
func()
print(money) # 1000
  • 局部修改全局名称空间中不可变类型的数据,需要使用关键字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) # [1, 2, 3, 4, 5, 113123, 666]
def func():
global s
s = 'jason'
res = s.strip('$')
l1.append(113123)
l1.append(666)
func()
print(l1) # [1, 2, 3, 4, 5, 113123, 666]
  • nonlocal 在内存局部名称空间修改外层局部名称空间中的不可变类型
def func1():
x = 1
l1 = [1,2]
def func2():
# nonlocal x
x = 999
l1.append(666)
func2()
print(x) # 1
print(l1) # [1, 2, 666]
func1()
def func1():
x = 1
l1 = [1,2]
def func2():
nonlocal x
x = 999
l1.append(666)
func2()
print(x) # 999
print(l1) # [1, 2, 666]
func1()

函数名的多种使用方式

  • 函数名也可以被用来多次赋值(函数名与变量名使用一致)

  • 函数名还可以当做函数的实参

  • 函数名还可以当做函数的返回值

  • 数名还可以当做容器类型里面的数据值

闭包函数

  • 定义:在一个内部函数中,对外部作用域的变量进行引用,(并且一般外部函数的返回值为内部函数),那么内部函数就被认为是闭包

  • 特征:

    • 定义在函数内部的函数
    • 内部函数使用了外部函数
  • 实际应用:

# 给函数体代码传值的方式一:通过形参传值
def func(xxx):
print(xxx)
# 给函数体代码传值的方式2:闭包函数
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 # home = outer(真正的函数名home)
def home():
'''我是home函数 我要热死了!!!'''
time.sleep(1)
print('from home')
return 'home返回值'
home()

多层装饰器

  • 语法糖功能:会自动将下面紧挨着的函数名当做参数传递给@符号后面的函数名(加括号调用)
  • 多个语法糖装饰一个函数名:从下往上执行最后一个语法糖才会做重命名操作
def outter1(func1): # func1 = wrapper2函数名
print('加载了outter1')
def wrapper1(*args, **kwargs):
print('执行了wrapper1')
res1 = func1(*args, **kwargs)
return res1
return wrapper1
def outter2(func2): # func2 = wrapper3函数名
print('加载了outter2')
def wrapper2(*args, **kwargs):
print('执行了wrapper2')
res2 = func2(*args, **kwargs)
return res2
return wrapper2
def outter3(func3): # func3 = 真正的index函数名
print('加载了outter3')
def wrapper3(*args, **kwargs):
print('执行了wrapper3')
res3 = func3(*args, **kwargs)
return res3
return wrapper3
# 语法糖功能:会自动将下面紧挨着的函数名当做参数传递给@符号后面的函数名(加括号调用)
@outter1 # index = outter1(wapper2) index是wrapper1
@outter2 # wrapper2 = outter2(wrapper3)
@outter3 # wrapper3 = outter3(真正的index函数名)
def index():
print('from index')
index()
# 加载了outter3
# 加载了outter2
# 加载了outter1
# 执行了wrapper1
# 执行了wrapper2
# 执行了wrapper3
# from 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: # for循环自带结束条件,不用手动加结束条件
if isinstance(i, int): # 判断i绑定的数据值是不是整型
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
# 1.先获取数据集中间那个数
middle_index = len(l1) // 2
middle_value = l1[middle_index]
# 2.判断中间的数据值与目标数据值孰大孰小
if target_num > middle_value:
# 3.说明要查找的数在数据集右半边 如何截取右半边
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, 999)
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) # 10
# 三元表达式编写
def max_num(a, b):
return a if a > b else b
res = max_num(1, 10)
print(res) # 10
  • 注意:

    • 仅限于二选一的情况并且不建议嵌套使用
    res = 123 if 10 > 5 else (222 if 0 else (666 if 1 == 2 else 666)) # 不建议嵌套使用
    • 在python中代码不是精简的越少越好,在精简的过程中还要保证代码的可读性

生成式

列表生成式

  • 概念:列表生成式是python内置的一种创建列表的方法,通过在[]内部执行一行for循环语句,将for循环所遍历到的元素添加到列表中

  • 语法结构:[表达式 for 变量 in 列表][表达式 for 变量 in 列表 if 条件]

  • 注意:列表生成式中只能出现for和if

# 给列表中所有的数据值加上_NB的后缀
name_list = ['jason', 'kevin', 'oscar', 'jerry', 'tony']
# 1.定义一个新的列表
new_list = []
# 2.循环原列表中所有的数据值
for name in name_list:
# 3.拼接_NB后缀
new_name = name + '_NB'
# 4.追加到新列表中
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) # {0: 'jason', 1: 'jason', 2: 'jason', 3: 'jason', 4: 'jason', 5: 'jason', 6: 'jason', 7: 'jason', 8: 'jason', 9: 'jason'}
new_dict = {i: 'jason' for i in range(10) if i == 6}
print(new_dict) # {6: 'jason'}

集合生成式

  • 概念:python集合生成式与列表生成式几乎是一模一样的,只需要将[]替换为{} 即可,在{}内执行一个for循环语句,for循环所遍历的元素自动添加到集合之中
  • 语法结构:{表达式 for 变量 in range()}{表达式 for 变量 in range() if 条件}
new_set = {i for i in range(10)}
print(new_set) # {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
new_set = {i for i in range(10) if i == 6}
print(new_set) # {6}

注意:元组没有生成式,它是后面重点讲解的迭代器知识(生成器)

匿名函数

  • 概念:匿名函数,也称为lambda函数,是没有定义名称的函数
  • 语法结构:lambda 形参:返回值(lambda函数可以具有任意数量的参数,但只能有一个表达式,不用写return,返回值就是该表达式的结果。Lambda函数可在需要函数对象的任何地方使用)
  • 注意:lambda 表达式创建的函数只能包含一条表达式
  • 案例
(lambda x: x + 1)(123) # 直接调用
res = lambda x: x + 1 # 命名调用
print(res(123))
  • 应用场景:通常都需要配合其他函数一起使用,用于减少代码

重要内置函数

max函数

  • max():最大值。如果值是字符串,则按字母顺序进行比较。如果值是字符串,则按字母顺序进行比较
# max函数与lambda函数(匿名函数)集合使用
dic = {
'jason': 100,
'aj': 123,
'Bason': 9999999,
'zero': 888
}
def index(k):
return dic.get(k)
# res = max(dic, key=lambda k: dic.get(k))
res = max(dic, key=index)
print(res) # Bason

min函数

  • min():最小值。如果值是字符串,则按字母顺序进行比较。如果值是字符串,则按字母顺序进行比较
# min函数与lambda函数(匿名函数)集合使用
dic = {
'jason': 100,
'aj': 123,
'Bason': 9999999,
'zero': 888
}
def index(k):
return dic.get(k)
# res = min(dic, key=lambda k: dic.get(k))
res = min(dic, key=index)
print(res) # jason

map函数

  • map():映射函数
# map函数与lambda函数(匿名函数)集合使用
l1 = [11, 22, 33, 44, 55, 66]
res = map(lambda x: x + 20, l1)
print(res) # <map object at 0x000001954B581100>
print(list(res)) # [31, 42, 53, 64, 75, 86]

filter函数

  • 定义和用法:过滤器,用法和map类似
# filter函数与lambda函数(匿名函数)集合使用
l1 = ['jason', 'kevin', 'oscar', 'tony']
res = filter(lambda a: a != 'jason', l1)
print(res) # <filter object at 0x00000195F21E6C70>
print(list(res)) # ['kevin', 'oscar', 'tony']

reduce函数

  • 定义和用法:会对参数序列中元素进行累积
# reduce函数与lambda函数(匿名函数)集合使用
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) # 106

zip函数

  • 定义和用法:组合对象,将对象逐一配对
n1 = [1, 2, 3]
n2 = ['jason', 'kevin', 'oscar']
res = zip(n1, n2)
print(res) # <zip object at 0x000002A9E38C7F40>
print(list(res)) # [(1, 'jason'), (2, 'kevin'), (3, 'oscar')]
n1 = [1, 2, 3, 4]
n2 = [5, 6, 7, 8]
n3 = 'jack'
res = zip(n1, n2, n3)
print(list(res)) # [(1, 5, 'j'), (2, 6, 'a'), (3, 7, 'c'), (4, 8, 'k')]
n1 = [1, 2, 3, 4, 5, 6, 7]
n2 = [5, 6, 7, 8]
n3 = 'jason'
res = zip(n1, n2, n3)
print(list(res)) # [(1, 5, 'j'), (2, 6, 'a'), (3, 7, 's'), (4, 8, 'o')]

练习题

1.编写一个用户认证装饰器
基本要求
执行每个函数的时候必须先校验身份 eg: jason 123
拔高练习(有点难度)
执行被装饰的函数 只要有一次认证成功 那么后续的校验都通过
函数:register login transfer withdraw
提示:全局变量 记录当前用户是否认证
is_login = {'is_login': False, }
# 2.编写装饰器
def login_auth(func_name):
def inner(*args, **kwargs):
# 2.3判断全局字典is_login对应的值是否是True
if is_login.get('is_login'):
# 2.4直接执行被装饰函数即可
res = func_name(*args, **kwargs)
return res
# 2.1假设是第一次调用 肯定需要校验用户身份
username = input('username>>>:').strip()
password = input('password>>>:').strip()
if username == 'jason' and password == '123':
res = func_name(*args, **kwargs)
# 2.2保存用户登录的状态
is_login['is_login'] = True
return res
else:
print('权限不够 无法执行')
return inner
# 1.定义函数
@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: # 'jason|123'
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)
 posted on   念白SAMA  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示