函数
一、函数的定义
1、什么是函数
函数就是封装了某一功能的代码
2、为什么要函数
因为 不利于方便读
代码冗余
维护性差 拓展性也差
3、怎么用函数
先定义(三种方式)
后调用(三种方式)
返回值(三种方式)
二、函数的定义与调用
1、函数的定义模板
def 函数名(参数1, 参数2)
"""文档描述"""
函数数据
return 值
2、函数的调用
def func(): # func 为内存的地址 func()则为函数的值
print("666")
func()
三、定义函数的三种形式
3.1 无参函数
def func(): # func 为内存的地址 func()则为函数的值
print("666")
func()
3.2 参数函数
def func(x, y): # x = 1, y = 2
print(x, y)
func(1, 2)
3.3 空函数
def func(x, y): # func 为内存的地址 func()则为函数的值
pass
func()
3.4 三大定义函数的应用场景
1、无参函数的应用场景
2、有参函数的应用场景
3、空函数的应用场景
当你做个项目的时候,写了很多函数确定功能的时候,可以用空函数先放那,然后根据实际情况再去修改
四、调用函数的三种形式
4.1 语句的形式直接调用
# 只调用函数
func(x, y)
4.2 表达式调用
1、赋值表达式
def func(x, y):
res = x + y
return res
res = func(1, 2)
print(res)
2、算术表达式
def func(x, y):
res = x + y
return res
res = func(1, 2) * 8
print(res)
4.3 函数调用可以当作参数
def func(x, y):
res = x + y
return res
res = func(func(1, 2),3)
print(res)
五、返回值的三种形式
5.1 不返回
def func(x, y):
print("123")
5.2 返回一个值
def func(x, y)
return 10
res = func()
print(res)
5.3 返回多个值
def func(x, y):
return 10, 8, [1, 2]
res = func()
print(res)
# 会以元组的方式返回
六、形参与实参
6.1 形参与实参
# 形参是函数定义时括号内的变量名,接受外界的值
# 实参则是函数调用时括号里的 可以是常量 变量 表达式的或者三者混合的传入的值
6.2 位置参数
1、什么是位置参数
位置参数实际上就是按照从左到右的顺序去传参,参数可以是形参也可以是实参
特点是 必须被传值 多一个不行 少一个不行
2、
def func(x, y) # x, y则是形参
print("123")
func(1, 2) # x = 1, y = 2,按照顺序传入了x, y
6.3 关键字参数
1、什么是关键字参数
关键字参数则是通过变量名 = 值 去准确传递,也可以说是关键字实参,这样就不用按照从左到右的顺序去传参数了
2、
def func(x, y)
print(x, y)
func(y=1, x=2)
3、注意
3.1 位置参数必须在关键字参数前,例如
def func(x, y, z)
print(x, y, z)
func(1, z=2, y=1)
3.2 不能重复给一个形参传值
6.4 默认形参
1、what
默认形参则是在定义函数时,就已经为形参赋值,这类形参称之为默认参数,当函数有多个参数时,需要将值经常改变的参数定义成位置参数,而将值改变较少的参数定义成默认参数。例如编写一个注册学生信息的函数,如果大多数学生的性别都为男,那完全可以将形参sex定义成默认参数
2、定义时就已经为参数sex赋值,意味着调用时可以不对sex赋值,这降低了函数调用的复杂度
# 需要注意:
默认参数必须在位置参数之后
默认参数的值仅在函数定义阶段被赋值一次
3、代码
def student(name, age, sex='male'):
print(name, age, sex)
student('Ame', 18)
student('Yuki', 18)
student('Egon', 18, 'female')
6.5 可变长参数
1.what
可变长参数分为两种 一是可变长形参 二是可变可变长实参
2.可变长形参
我们可以用*args **kwargs来接受不固定的值的长度,比如我们不知道要传入多少参数,我们就可以用这两个来接受传入的参数
# *args会传入后以元组的方式来保存
# **kwargs参数传入后会以字典的方式去保存
3.可变长实参
# 打散
我们用元组传入后 元组的数据会以打散后位置参数传入形参
字典方式传入后,字典就会以关键字实参模式传入形参
4.代码1
def func(*args, **kwargs):
print(args, kwargs)
func(1, 2, 3, 4, 5, 4, x=1, y=2, z=3, c=4)
"""
(1, 2, 3, 4, 5, 4) {'x': 1, 'y': 2, 'z': 3, 'c': 4}
"""
5.代码2
def index(x, y, z):
print(x, y, z)
def wrapper(*args, **kwargs):
index(*args, **kwargs)
wrapper(1, z=2, y=3)
# 分析:首先将实参传值给形参:*args = (1,) **kwargs = {'y':3,'z':2}
# 第二步:这里是直接把wrapper(*args,**kwargs)中的形参给到了index(*args, **kwargs)的实参,
# 即index(*(1,),**{'y':3,'z':2}),这里再次将其打散,然后绑定给x,y,z
七、名称空间与作用域
7.1 名称空间
"""
名称空间是 在内存栈区中分好不同的名称空间,这样就可以存放一样的变量名的空间,所以因为有名称空间的存在,我们才可以在不同的空间去使用相同的名字
"""
7.2 内置名称空间
"""
存放的名字: 从python解释器内置存放的名字
存活的周期: 从python打开即生成 python关闭则死亡
"""
# 例如
print
input
7.3 全局名称空间
"""
存放的空间: 既不在函数内,也不再解释器内置里,都为全局名称空间
存活的周期: 文件的执行即开始,文件的执行结束为死亡
"""
# 例如1
def func()
x = 5
y = 1
func() # func既不是函数内 也不是内置内,所以为全局名称空间,其他的都一样
# 例如2
x = 250
y = 38
"""
x和y也是全局名称空间里面的,全局也皆可调用
"""
# 例如3
if 3>1:
c = 10
"""
这个c也是全局名称空间里面的,虽然他不是顶格写的,但是也是全部皆可调用
"""
7.4 局部名称空间
"""
存放的空间: 是函数内创造的
存货的周期: 函数调用开始出生 函数调用结束死亡
"""
# 例如
def func() # x, y则在局部名称空间 定义时出生
x = 5
y = 1
func() # 调用时死亡
7.5 名称空间的出生顺序和销毁顺序和查找顺序
1.出生顺序
内置名称空间>全局名称空间>局部名称空间
2.销毁顺序
局部名称空间>全局名称空间>内置名称空间
3.查找顺序
# 如果在局部名称空间
局部名称空间--------全局名称空间--------内置名称空间
# 如果在全局名称空间
全局名称空间--------内置名称空间
7.6 全局作用域
"""
全局存活
全局有效: 即所有的函数都能调用他
"""
7.7 局部作用域
"""
局部存活
局部有效: 即只有函数内才能调用他
"""
八、函数对象
# 函数对象的特点精髓就是可以把函数看成变量去调用
比如
def func(x):
print(x)
res = func()
九、 函数嵌套
# 函数嵌套调用就是在一个函数内再调用一个函数
# 函数嵌套定义就是在一个函数内再定义一个函数
def max1(x, y)
if x > y:
return x
else:
return y
def max(a, b, c, d):
res1 = max1(a, b)
res2 = max1(res1, c)
res3 = max1(res2, d)
print(res3)
max(1, 156, 31, 456)
十、闭包函数
1.1.什么是闭包函数
闭包函数 = 名称空间与作用域 + 函数对象 + 函数嵌套
其实闭包函数就是一个传参方式
1.2.为什么要用闭包函数
万一函数的参数被占用了,我们应该怎么传参就用闭包函数
1.3.怎么用闭包函数
def func(x):
x = 100
def wrapper():
print(x)
return wrapper
f = func()
f()
"""
这就是闭包函数,wrapper被包起来了,只能用到func的x,外界都没有用
"""
十一、装饰器
11.1、 装饰器的定义(what why how)
1.什么是装饰器
装饰器就是装饰某一个东西的工具
2.为什么要用装饰器
因为在源函数代码不改变调用方法&原代码给原函数添加新功能,装饰器是最好的解决工具
3.怎么用装饰器
详见下面
11.2、 装饰器的实现思路
"""
现有一个原函数index,想要为其添加一个时间差的功能,我们应该怎么做呢
"""
# 方案一 这就已经修改了他的原代码了
import time
def index(x):
start_time = time.time()
print(f'welcome to index {x}')
time.sleep(3)
print(time.time() - start_time)
index(1)
# 方案二 没有修改原函数的代码&调用,但是代码冗余
def index(x):
print(f'welcome to index {x}')
time.sleep(3)
start_time = time.time()
index(1)
print(time.time() - start_time)
start_time = time.time()
index(1)
print(time.time() - start_time)
# 方案三 解决了代码冗余问题,但是原函数的调用我们修改了,而且wrapper写死了,只能调用index
def index(x):
print(f'welcome to index {x}')
time.sleep(3)
def wrapper():
start_time = time.time()
index(1)
print(time.time() - start_time)
wrapper()
# 方案四 我们先搞定调用index函数写死问题
def index(x):
print(f'welcome to index {x}')
time.sleep(3)
def outter(func):
def wrapper():
start_time = time.time()
func(1)
print(time.time() - start_time)
return wrapper
index = outter(index)
index()
# 方案五 然后在搞定index函数参数问题
def index(x):
print(f'welcome to index {x}')
time.sleep(3)
def outter(func):
def wrapper(*args, **kwargs):
start_time = time.time()
func(*args, **kwargs)
print(time.time() - start_time)
return wrapper
index = outter(index)
index(11)
# 方案六 在搞定index返回值的问题,这样就把wrapper伪装成index伪装的很好了
def index(x):
print(f'welcome to index {x}')
time.sleep(3)
return 123
def outter(func):
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
print(time.time() - start_time)
return res
return wrapper
index = outter(index)
res = index(11)
print(res)
# 方案七 在让wrapper更像index一点
def index(x):
"""
这是index的配置信息
"""
print(f'welcome to index {x}')
time.sleep(3)
return 123
def outter(func):
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
print(time.time() - start_time)
return res
wrapper.__name__ = index.__name__
wrapper.__doc__ = index.__doc__
return wrapper
index = outter(index)
res = index(11)
print(res)
# 语法糖
def outter(func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
print(time.time() - start_time)
return res
return wrapper
@outter
def index(x):
"""
这是index的配置信息
"""
print(f'welcome to index {x}')
time.sleep(3)
return 123
res = index(11)
print(res)
11.3、装饰器的总结模板
# 无参装饰器
def outter(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
# 有参装饰器
def outter(x):
def foo(func):
def wrapper(*args, **kwargs):
res = func(*args, **kwargs)
return res
return wrapper
return foo
11.4 多个装饰器
import time
def deco1(func):
print('----deco1')
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
print(time.time() - start_time)
return res
return wrapper()
def deco2(func):
print('----deco2')
def wrapper(*args, **kwargs):
start_time = time.time()
res = func(*args, **kwargs)
print(time.time() - start_time)
return res
return wrapper()
@deco1
@deco2
def index(x):
print(f'{x}')
'''
当多个装饰器叠加的时候 例如这样是先从上而下去执行,但是返回值则是从下而上的去返回 所以deco2的返回值是在deco1,deco1的返回值是在真正的index上
'''
十二、 有参装饰器
def login():
name = input('name is :').strip()
pwd = input('pwd is :').strip()
if name == 'ycc' and pwd == '123':
print('login successful')
else:
print('name or pwd error')
def auth(db_type):
def deco(func):
def wrapper(*args,**kwargs):
if db_type == 'file':
login()
print('基于文件验证')
# res = func(*args,**kwargs)
# return res
elif db_type == 'mysql':
login()
print('基于数据库验证')
# res = func(*args, **kwargs)
# return res
elif db_type == 'ldap':
login()
print('基于ldap验证')
# res = func(*args, **kwargs)
# return res
res = func(*args, **kwargs)
return res
return wrapper
return deco
@auth(db_type='file')
def index(x, y):
print(x, y)
@auth(db_type='mysql')
def home(name):
# print('welcome %s to home page'%name)
print(f'welcome {name} to home page')
return name
@auth(db_type='ldap')
def transfer():
print('transfer')
index(1, 2)
name = home('ycc')
print(name)
十三、可迭代对象与迭代器对象
1.什么是可迭代对象
里面有内置的__iter__方法的就是可迭代对象
'abc'.__iter__
[1, 2, 3].__iter__
{'x':1, 'y':2}.__iter__
# 调用一个可迭代对象的__iter__()会得到一个返回值,改返回值是迭代器对象
2.迭代对象对象的特点:
2.1 内置有__next__方法
iter_s = s.__iter__() # iter_s = iter(s)
print(iter_s.__next__())
dic = {'x':1, 'y':2}
iter_dic = iter(dic)
print(iter_dic.__next__())
十四、迭代器
1.什么是迭代器
迭代器就是用来迭代取值的工具
什么是迭代?
迭代就是一个重复的过程,但是每次重复都是基于上一次的结果进行的
# 单纯的重复不叫迭代
while True:
1+1
# 下面才是迭代的过程
count = 1
while count < 5:
print(count)
count += 1
2.为何要用迭代器
优点
(1) 找到一种通用的迭代取值方案->for循环
(2)惰性计算,节省内存
缺点:
1、不能取指定的值,只能往后next
2、迭代器对象是一次性的:一个迭代器对象值取干净了,不能继续取了
3.可迭代对象
3.1 内置有__iter__方法的对象都是可迭代对象
"abc".__iter__()
[1, 2, 3].__iter__()
(1, 2, 3).__iter__()
{"x": 1, "y": 2}.__iter__()
{1, 2, 3, 4}.__iter__()
open('a.txt', mode='r').__iter__()
open('a.txt', mode='r').__next__()
# 调用可迭代对象的__iter__()会得到一个返回值,该返回值是迭代器对象
迭代对象对象的特点:
1、内置有__next__()方法
2、内置有__iter__()方法
s = "abc"
iter_s = s.__iter__() # iter_s = iter(s)
print(iter_s.__next__()) # print(next(ites_s))
print(iter_s.__next__())
print(iter_s.__next__())
print(iter_s.__next__())
dic = {"x":1,"y":2}
iter_dic = dic.__iter__()
print(iter_dic.__next__())
print(iter_dic.__next__())
4. for工作原理
4.1 调用in后那个对象的__iter__方法,得到一个迭代器对象iter_names
4.2 x=next(iter_names),然后运行循环体代码
4.3 重复步骤2,直到取完值,抛出异常,for循环会帮我们捕捉异常结束循环
5.for循环里面的内部原理
names = ['egon', 'tom', 'jack', 'lili', 'ttt']
iter_s1 = iter(names)
print(next(iter_s1))
print(next(iter_s1))
print(next(iter_s1))
print(next(iter_s1))
print(next(iter_s1))
iter_s2=iter(names)
print(next(iter_s2))
print(next(iter_s2))
print(next(iter_s2))
print(next(iter_s2))
print(next(iter_s2))
# 下面的第二个for循环其实是取不出值的
iter_names = iter(names)
for x in iter_names :
print(x)
print('='*50)
for x in iter_names :
print(x)
十五、生成器&yield
1. 什么是生成器
生成器就是自定义的迭代器
2. 为什么要用生成器
因为生成器也就是迭代器是惰性计算不浪费内存,所以我们有时候可以自己自定义一个迭代器
3. 如何定义生成器
# 这个就得用到我们的秘籍-----yield
def func():
print('one...')
yield 111
print('two...')
yield 222
print('three...')
yield 333
print('fourth')
# 函数内但凡出现yield关键字,再调用函数不会立即执行函数体代码,而会返回一个
# 生成器对象,生成器对象本质就是一个自定义的迭代器对象
4. yield 对比 return
"""
相同点:都可以返回任意类型、任意个数的值
不同点:return只能返回值一次值,函数就立即结束
yield可以返回多次值,yield可以暂停函数的运行
"""
十六、函数递归
1. 什么是函数递归
就是定义一个函数,然后再函数内部调用自己,说白了就是个循环
2. 函数递归可以分为两个阶段
2.1 回溯 就是去要的过程
2.2 递推 要到了回来的过程
3. 函数递归应用
l = [1, [2, [3, [4, [5, [6, [7, [8]]]]]]]]
def func(l):
for items in l:
if type(items) is list:
func(items)
else:
print(items)
func(l)
十七、三元表达式
# 过于麻烦的普通代码
def func():
if x > y:
return x
else:
return y
# 三元表达式
x if x > y else y
# 三元表达式的格式
达成条件的返回的值 if 条件 条件没有达成的返回值
十八、各种生成式
18.1 列表生成式
"""
列表生成式
"""
# 正常思路
l = ["xxx_dsb", "aaa_dsb", "bbb_dsb", "ccc_dsb", "yuki"]
new_l = []
for i in l:
if i.endswith("dsb"):
new_l.append(i)
print(new_l)
# 列表生成式
l = ["xxx_dsb", "aaa_dsb", "bbb_dsb", "ccc_dsb", "yuki"]
new_l = [i for i in l if i.endswith("dsb")]
print(new_l)
# 练习 1 把所有小写字母换成大写字母 2 把所有名字去掉后缀
l = ["xxx_dsb", "aaa_dsb", "bbb_dsb", "ccc_dsb", "yuki"]
new_1 = [name.upper() for name in l]
new_l = [i.replace("_dsb", '') for i in l if "_dsb" in i]
18.2 字典生成式
keys = {"aaa", 'bbb', 'ccc'}
{k: None for k in keys}
18.3 集合生成式
keys = {"aaa", 'bbb', 'ccc'}
{k for k in keys}
18.4 生成器生成式
# 把一个文件所有的字符总和取出来
with open("stock_data.txt", mode="rt", encoding="UTF-8") as file:
print(sum(len(line) for line in file) # 节约了内存 提高了效率
十九、 二分法
需求:如果一个列表储存着从小到大的数字
然后从中需要找一个数字
1.for循环
list = [-6, -1, 2, 5, 6, 10, 12, 16, 20, 25, 28, 30, 65, 98]
need_num = 25
for i in list:
if i == need_num:
break
print(i)
"""
这样是可以找出来,但是这样假如列表有很多元素,比如一千万个
这样寻找会效率非常低,所以应该改进
"""
2.二分法
# 思路模板
list = [-6, -1, 2, 5, 6, 10, 12, 16, 20, 25, 28, 30, 65, 98]
need_num = 25
def func():
median = 中间值
if 中间值 > need_num:
就要从左边开始寻找
list = 左边的列表
def func()
elif 中间值 < need_num:
就要从右边开始寻找
list = 右边的列表
def func()
else:
print(find it)
# 实现
list = [-6, -1, 2, 5, 6, 10, 12, 16, 20, 25, 28, 30, 65, 98]
need_num = 30
def func(need_num, list):
print(list)
median = len(list) // 2
if list[median] > need_num:
list = list[0:median]
print(list)
func(need_num, list)
elif list[median] < need_num:
list = list[median:]
print(list)
func(need_num, list)
else:
print("find it")
func(need_num, list)
二十、匿名函数
匿名函数的应用场景
任何只需要用到一次函数,用完即可不要 就可以用匿名函数
lambda x,y: x+y
匿名函数与有名函数有相同的作用域,但是匿名意味着引用计数为0,使用一次就释放,所以匿名函数用于临时使用一次的场景,匿名函数通常与其他函数配合使用,我们以下述字典为例来介绍它
salaries={
'siry':3000,
'tom':7000,
'lili':10000,
'jack':2000
}
要想取得薪水的最大值,我们可以使用内置函数max
>>> max(salaries)
'tom'
>>> min(salaries)
'jack'
内置max和min都支持迭代器协议,工作原理都是迭代字典,取得是字典的键,因而比较的是键的最大和最小值,而我们想要的是比较值的最大值与最小值,于是做出如下改动
# 函数max会迭代字典salaries,每取出一个“人名”就会当做参数传给指定的匿名函数,然后将匿名函数的返回值当做比较依据,最终返回薪资最高的那个人的名字
>>> max(salaries,key=lambda k:salaries[k])
'lili'
# 原理同上
>>> min(salaries,key=lambda k:salaries[k])
'jack'
同理,我们直接对字典进行排序,默认也是按照字典的键去排序的
>>> sorted(salaries)
['jack', 'lili', 'siry', 'tom']