函数 def
函数概述
Python 函数主要的作用就是打包代码,可以最大程度地实现代码重用,减少冗余的代码,将不同功能的代码段进行封装、分解,从而降低结构的复杂度,提高代码的可读性。
函数的基本使用
函数的创建和调用
| Python |
|---|
| # 创建
def fun():
pass
# 使用
fun()
|
函数的参数类型
函数的参数分为: 形式参数(形参)和实际参数(实参)。
- 形参: 函数定义时候的参数名称。
- 实参: 函数调用时实际传入的参数。
| Python |
|---|
| # 创建包含参数的函数,如果有多个参数括号内使用逗号隔开,传递多个参数
def fun(name):
print(name)
# 使用
fun("hello word") # --> hello word
|
函数的返回值
使用关键字 return,将函数需要返回的值进行返回,只要函数执行返回语句,就会立即返回,而不会执行之后的代码。如果函数没有返回值,那么函数默认也会返回一个 None,函数如果有多个返回值,将以元组的方式返回。
| Python |
|---|
| def fun():
return 11
print(fun()) # --> 11
def fun1():
pass
a = fun1()
print(a) # --> None
|
函数的参数定义
函数的参数就是定制的接口,只要按照函数的要求,将指定的参数正确传递,函数就可以按指定的程序运算返回最终的结果,函数的参数可以分为:位置参数和关键之参数和默认参数以及收集参数。
- 位置参数: 只需要按照正确的位置传递参数即可。
- 关键字参数: 只需要知道关键字对关键字进行赋值传递。
- 默认参数: 如果函数的默认参数没有传递参数就会执行默认参数进行传递。
- 收集参数: 定义函数的时候在参数前加
*表示收集参数,本质是通过元组进行打包,将传入的多个参数打包到元组里,如果定义参数的时候有收集参数和其他参数,此时传入参数的时候,其他参数都需要使用关键字参数赋值,不然都会打包到收集参数中,收集参数同样可以将传入的参数打包成字典,此时传递参数就必须使用关键字参数。
- 解包参数: 在实参中使用星号表示解包参数,其中包含一个星号和两个星号,一个星号表示元组的形式解包,可以是元组也可以是列表,两个星号表示字典的形式关键字解包,字典的键对应函数的关键字,必须一一对应。
注意: 位置参数必须在关键字参数之前,如果函数创建使用了默认参数,需要将默认参数放到参数最后。
使用help()可以对函数进行查看,函数参数里面有 /,表示该符号左边必须使用位置参数,不可以使用关键字参数。
如果函数参数有*则表示左边既可以使用关键字参数也可以使用位置参数,右边只可以使用关键字参数。
| Python |
|---|
| # 位置参数
def fun1(a, b):
print(a*b)
fun1(1, 2) # --> 2
# 关键字参数
def fun2(a, b):
print(a*b)
fun2(a=1, b=2) # --> 2
# 默认参数
def fun3(a, b=2):
print(a*b)
fun2(a=1) # --> 2
# 包含/的函数参数
def fun4(a, /, b):
print(a*b)
fun4(1, b=4) # --> 4
# 包含*的函数参数
def fun5(a, *, b):
print(a*b)
fun5(1, b=4) # --> 4
# 收集参数
def fun6(*arg):
print(arg)
fun6(1, 2, 3) # --> (1, 2, 3)本质是元组
# 如果存在收集参数和其他参数其他参数需要使用关键字参数,这个时候可以使用*强制后面的参数使用关键字参数
def fun7(*arg, *, a, b):
print(arg, a, b)
fun7(1, 2, 3, a=7, b=9) # --> (1, 2, 3) 7 9
# 收集参数,**参数打包成字典进行传递
def fun8(**arg):
print(arg)
fun8(a=1, b=2) # --> {"a": 1, "b":2}
# 综合起来
def fun9(a, *b, **c):
print(a, b, c)
fun9(1, 2, 3, a = 1, b = 2) # --> 1 (2, 3) {"a":1,"b":2}
# 解包参数
# 一个星号
a = (1, 2, 3)
def fun10(a, b, c):
print(a, b, c)
fun10(*a) # --> 1 2 3
# 两个星号
a = {"a": 1,"b": 2,"c": 3}
def fun10(a, b, c):
print(a, b, c)
fun10(*a) # --> 1 2 3
|
作用域
作用域概述
一个变量可以被访问的范围,通常作用域总是由它在代码中的赋值位置决定的。
- 局部作用域: 如果一个变量定义在一个函数内部,那个这个变量称为局部变量。
- 全局作用域: 如果在任何函数的外部定义一个变量,那这个变量称为全局变量,如果在函数内部和函数外部存在同名变量,函数内部的变量会覆盖函数外部的变量。
global 语句
修改全局变量的值:
| Python |
|---|
| x = 999
def fun():
global x
x = 521
print(x)
|
嵌套函数
嵌套函数概述
内部函数声明完成后需要在外部函数引用,内部函数可以访问外部函数变量,无法直接进行修改外部函数的变量值,如果一定要修改需要使用 nonlocal 语句进行修改。
| Python |
|---|
| def funa():
x = 999
def funb():
x = 520
print("funb x =", x)
funb()
print("funa x =", x)
# funb x = 520
# funa x = 999
|
nonlocal语句
修改外部函数变量值,如果在嵌套函数中,内部函数使用该语句,此时需要外部函数函数内部有该变量,不然在内部函数使用该语句会报错,因为该语句是为了内部函数使用和修改外部变量。
| Python |
|---|
| def funa():
x = 999
def funb():
nonlocal x = 520
print("funb x =", x)
funb()
print("funa x =", x)
# funb x = 520
# funa x = 520
|
LEGB 规则,作用域规则
| 级别由高到低 |
作用域 |
| 1 |
Local(局部作用域) |
| 2 |
Enclosed(嵌套函数的外部作用域) |
| 3 |
Global(全局作用域) |
| 4 |
Build-In(内置函数),不可以和 python 内置函数重名,否则变量会覆盖内置函数,内置函数作用域最低 |
闭包函数
内部函数做为外部函数的返回值,通过修改外部函数的变量值达到变量储存,实时修改变量, 函数的调用和创建需要使用括号其他时候都不需要使用括号,对于嵌套函数外层函数的作用域是会通过某种形式保留下来,闭包函数又叫做工厂函数,闭包函数必须由嵌套函数实现,闭包函数可以将嵌套函数的内部函数直接在程序中访问。
- 利用嵌套函数的外层作用域具有记忆能力这个特性,让数据保留在外层函数的参数或者变量中。
- 将内层函数做为返回值返回了,这样就可以从外部间接调用内部函数。
| Python |
|---|
| # 例一
def funa(x=1):
def funb():
print(x)
return funb
test = funa()
test()
# 例二
def a(x):
def b(y):
print(x ** y)
return b
test1 = a(2)
test2 = a(3)
test1(2) # --> 2的平方 4
test2(2) # --> 3的平方 9
# 例三,可以进行坐标的实时更新
def a(x=0, y=0):
def b(x1, y1):
nonlocal x, y
x += x1
y += y1
print(x, y)
return b
test = a()
test(2, 3)
test(3, 4)
|
装饰器
将一个函数做为参数传递给另一个函数,可以利用装饰器语法糖更简单实现,可以发现修饰器其实是建立在闭包的基础上,增加语法糖缩写一些代码实现修饰器的效果。
| Python |
|---|
| import time
# 不适用修饰器进行实现一个函数做为另一个函数参数
def a(fun):
print("开始调用函数。。")
start = time.time()
fun()
print("函数调用结束··")
stop = time.time()
print(f"函数共调用时间为", stop - start)
def b():
print("hello word")
time.sleep(2)
a(b)
# 使用修饰器进行实现
def time_master(fun):
def test():
print("开始调用函数。。")
start = time.time()
fun()
print("函数调用结束··")
stop = time.time()
print(f"函数共调用时间为", stop - start)
return test
@time_master
def b():
print("hello word")
time.sleep(2)
b()
# 实际使用闭包函数
def time_master(fun):
def test():
print("开始调用函数。。")
start = time.time()
fun()
print("函数调用结束··")
stop = time.time()
print(f"函数共调用时间为", stop - start)
return test
def b():
print("hello word")
time.sleep(2)
a = time_master(b)
a()
# 同样可以进行多次调用,此时需要对应多个语法糖,每个执行函数对应一个语法糖
def time_master(fun):
def test():
print("开始调用函数。。")
start = time.time()
fun()
print("函数调用结束··")
stop = time.time()
print(f"函数共调用时间为", stop - start)
return test
@time_master
def b():
print("hello word")
time.sleep(2)
@time_master
def c():
print("hello")
time.sleep(4)
b()
c()
# 同样可以多个装饰器调用一个函数
def add(func):
def inner():
x = func()
print(1)
return x + 1
return inner
def cube(func):
def inner():
x = func()
print(2)
return x * x * x
return inner
def square(func):
def inner():
x = func()
print(3)
return x * x
return inner
@ add
@ cube
@ square
def test():
return 2
print(test())
# 结果为65,此时程序先执行最下面的语法糖,然后继续向上
# 装饰器传入参数
def time_master(arg):
def test1(fun):
def test2():
print("开始调用函数。。")
start = time.time()
fun()
print("函数调用结束··")
stop = time.time()
print(f"{arg}函数共调用时间为", stop - start)
return test2
return test1
@time_master(arg="b")
def b():
print("hello word")
time.sleep(2)
@time_master(arg="c")
def c():
print("hello")
time.sleep(4)
b()
c()
# 不使用装饰器拆解开来
def time_master(arg):
def test1(fun):
def test2():
print("开始调用函数。。")
start = time.time()
fun()
print("函数调用结束··")
stop = time.time()
print(f"{arg}函数共调用时间为", stop - start)
return test2
return test1
def b():
print("hello word")
time.sleep(2)
def c():
print("hello")
time.sleep(4)
b = time_master(arg="b")(b)
c = time_master(arg="c")(c)
b()
c()
|
| Python |
|---|
| # 题目要求:
# 请在此处补充装饰器 type_check() 的代码
def type_check(aa):
def test(fun):
def test2(arg):
if type(arg) == aa:
return fun(arg)
else:
return "参数错误"
return test2
return test
print("<<<--- 测试整数 --->>>")
@type_check(int)
def double(x):
return x * 2
print(double(2)) # 这里打印结果应该是 4
print(double("2")) # 这里打印结果应该是 “参数类型错误”
print("\n<<<--- 测试字符串 --->>>")
@type_check(str)
def upper(s):
return s.upper()
print(upper('I love FishC.')) # 这里打印结果应该是 I LOVE FISHC
print(upper(250)) # 这里打印结果应该是 “参数类型错误”
|
lambda 表达式
lambda 表达式又称为匿名函数,建议处理简单的函数模型,因为该表达式是一行代码,lambda 可以直接放在列表里,同样也直接可以当成函数写入需要传递函数的参数里。
语法: lambda arg1, arg2, ... argN : expression,其中 arg 为函数的参数,expression 为代码块和返回值
传统语法:
| Python |
|---|
| def <lambda>(arg1, arg2, ... argN):
return expression
|
| Python |
|---|
| # 例一
# 普通函数创建
def fun(x):
return x * x
# 使用lambda表达式创建
test = lambda x : x * x
# 调用
fun(3)
test(3)
# 例二
# 将lambda表达式放在列表中使用
a = [lambda x : x * x, 1, 2]
a[0](a[1]) # --> 1
a[0](a[2]) # --> 4
# 例三
# 将lambda表达式当作函数参数赋值
test = map(lambda x : ord(x) + 10, "aaa")
print(list(test)) # --> [107, 107, 107]
|
| Python |
|---|
| # 装饰器不使用lambda表达式
def add(func):
def inner():
x = func()
return x + 1
return inner
def cube(func):
def inner():
x = func()
return x * x * x
return inner
def square(func):
def inner():
x = func()
return x * x
return inner
@add
@cube
@square
def test():
return 2
print(test())
# 使用lambda表达式
@lambda func: lambda x=func(): x + 1
@lambda func: lambda x=func(): x * x * x
@lambda func: lambda x=func(): x * x
def test():
return 2
print(test())
|
| Python |
|---|
| # 不使用lambda表达式
power = {"吕布":999, "关羽":888, "刘备":666, "张飞":900, "赵云":789, "不二如是":999}
# 请 lambda 表达式和 filter() 函数配合,替换下面的代码
greater = []
for k, v in power.items():
if v > 800:
greater.append((k,v))
# 请 lambda 表达式和 filter() 函数配合,替换下面的代码
print(greater)
[('吕布', 999), ('关羽', 888), ('张飞', 900), ('不二如是', 999)]
# lambda表达式
power = {"吕布":999, "关羽":888, "刘备":666, "张飞":900, "赵云":789, "不二如是":999}
greater = list(filter((lambda x: x[1] > 800), power.items()))
print(greater)
|
| Python |
|---|
| >>> members = {
"鱼C工作室" : {"小甲鱼":83, "不二如是":89, "二师兄":64, "小师妹":75, "鱼小二":96},
"复仇者联盟" : {"钢铁侠":85, "绿巨人":39, "黑寡妇":82, "鹰眼":73, "雷神":99},
"奥特曼家族" : {"迪迦":99, "艾斯":84, "泰罗":63, "佐菲":78, "赛文":78}}
>>>
>>> # 请在此处添加一行代码,完成题目要求,并将结果保存在变量 x 中
>>>
>>> print(x)
['鱼C工作室:二师兄', '复仇者联盟:绿巨人', '奥特曼家族:泰罗']
# 如下
t = [':'.join((i, min(members[i].items(), key=lambda x: x[1])[0])) for i in members]
# 分析
# 采用了列表推导式的方法,先进性字典键的循环,从而得到对应的值,然后进行返回字典对象
# 利用min函数求出字典中最小值,然后使用lambda表达式去取对应的键值
# 使用join函数将最开始的i(键)和最小的键值进行组合最终得到新的列表
|
生成器
生成器是含有 yield 语句(或 yield 表达式)的函数所返回的对象。也就是说,要创建一个生成器,我们首先要定义一个函数,该函数内将使用 yield 表达式或 yield 语句来表示每次生成的值。该函数的返回值是一个生成器对象,通过把这个函数的返回值赋给一个变量,我们就得到了一个生成器对象变量,对这个对象进行 send 和 next 等操作,即可实现生成器的功能。
定义了一个生成器,只有在每次调用的时候才会去执行输出对应生成器的值,这个就跟 return 语句不一样了,return 语句一执行,就不会管后面的语句了,yield 语句是 “打断并返回,然后下次执行就从下一条语句继续”。
| Python |
|---|
| # 定义一个简单的生成器
def test():
i = 0
while i <= 5:
yield i
i += 1
test1 = test()
for x in test1:
print(x)
# 可以使用next()函数进行输出生成器值
test2 = test()
next(test2)
next(test2)
next(test2)
# 全部输出在进行输出会报错,生成器不可以使用下标索引
# 使用生成器实现斐波那契数列
def aa():
x = 0
y = 1
while True:
yield x
x, y = y, x + y
test = aa()
for i in test:
print(i)
# 如果不设置循环结束语句会一直进行输出
# 生成器的调用,如下,111会被输出,for循环每调用一次生成器就会输出一个对应的i,当while循环结束对应输出了print111
def aa():
i = 0
while i <= 5:
yield i
i += 1
print(111)
test = aa()
for i in test:
print(i)
|
生成器表达式
生成器表达式是每次访问产生一个结果;列表推导式是将所有的结果创建为一个列表。
语法: (expression for target in iterable)和(expression for target in iterable if condition)
| Python |
|---|
| a = (i for i in range(10))
for x in a:
print(x)
|
递归函数
函数调用自身的行为称为递归,所以要想正确调用递归,函数执行需要有一个结束条件,并且每次调用都要向这个结束条件进行推进。
注意递归的算法,会将所有函数的最后一个返回值计算完,然后再一层一层向上计算。
| Python |
|---|
| # 例一
def test(n):
if n > 0:
print(n)
n -= 1
test(n)
test(5)
# 例二,求一个数的阶乘 --> 3 3*2*1
# 迭代
def test(n):
x = n
for i in range(1, n):
x *= i
return x
print(test(12))
# 递归
def test1(n):
if n == 1:
return n
else:
return n * test1(n-1)
print(test1(12))
# 例三,斐波那契数列
# 迭代
def test(n):
a = 1
b = 1
while n > 1:
a, b = b, a+b
n -= 1
return a
print(test(5))
# 递归
def test1(n):
if n == 1 or n ==2:
return 1
else:
return test(n-1) + test(n-2)
print(test1(12))
|
尾递归
执行效率要比普通递归高。
| Python |
|---|
| # 计算数的阶和 5 --> 1+2+3+4+5
# 普通递归
def test(n):
if n == 0:
return n
else:
return n + test(n - 1)
print(test(3))
# 尾递归
def test1(n, num=0):
if n == 0:
return num
else:
return test1(n - 1, num + n)
print(test1(3))
|
函数注释定义
函数文档
使用 help 可以查看函数文档,进而快速的了解函数的功能。必须要写在函数的开头,否则不起作用。
| Python |
|---|
| def test():
"""
解释内容
"""
return
# 使用help(test)进行查看
|
类型注释
| Python |
|---|
| def test(a:str, b:int) -> str:
return a*b
# 注意这里仅仅是注释如果传入的参数并不是期望传递的参数python不会报错阻止
# 使用默认参数
def test1(a:str = "a", b:int = 2) -> str:
return a*b
# 期望参数是整数列表
def test1(a:list[int], b:int = 2) -> list:
return a*b
# 期望参数是字典类型,括号第一个为键第二个为键值
def test1(a:dict[str, int], b:int = 2) -> list:
return list(a.keys())*b
|
内省
内省是程序运行前对自身进行检测的一种机制,Python 是通过特殊的属性来实现内省的。
| Python |
|---|
| def test():
pass
# 获取自身函数名
test.__name__
# 返回函数类型注释,返回的结果为字典,{'a': 'x', 'b': 11, 'c': <class 'list'>, 'return': 9}
test.__annotations__
# 查看函数文档,可以使用print进行格式化输出
test.__doc__
|
高阶函数
高阶函数定义
当一个函数做为另一个函数的参数进行传递,这个时候另外的这个函数就称为高阶函数。装饰器就是一个高阶函数,其中 map、max、min 都是高阶函数。
可以使用 functools 实现高阶函数功能。
| Python |
|---|
| import functools
def test(x, y):
return x+y
functools.reduce(test, [1, 2, 3, 4, 5]) # --> 15
# 等价于
test(test(test(test(1, 2), 3), 4), 5)
# 使用lambda表达式通过该模块计算10的阶乘
functools.reduce(lambda x, y: x*y, range(1, 11)) # --> 3628800
|
偏函数
偏函数是对函数进行二次包装,通常是将现有的函数部分参数预选给绑定,从而得到一个新的函数,该函数就是偏函数。其实就是将函数的多个参数拆分多次进行传递。同样适用模块 functools。
| Python |
|---|
| import functools
square1 = functools.partial(pow, exp=2)
square1(2) # --> 4
square1(3) # --> 9
square2 = functools.partial(pow, exp=3)
square2(2) # --> 8
square2(3) # --> 27
|
@wraps装饰器
| Python |
|---|
| def time_master(fun):
def test():
print("开始调用函数。。")
start = time.time()
fun()
print("函数调用结束··")
stop = time.time()
print(f"函数共调用时间为", stop - start)
return test
@time_master
def b():
print("hello word")
time.sleep(2)
b()
# 此时使用内省获取函数名为 b.__name__ --> test
# 此时并不是函数b
# 使用修饰器@wraps来修饰修饰器可以解决这一问题
import functools
def time_master(fun):
@functools.wraps(fun)
def test():
print("开始调用函数。。")
start = time.time()
fun()
print("函数调用结束··")
stop = time.time()
print(f"函数共调用时间为", stop - start)
return test
@time_master
def b():
print("hello word")
time.sleep(2)
b()
# 此时再去执行发现 b.__name__ --> b
|