零基础学Python 基础班 第十一章 函数 function
第一章 函数 function
1 函数简介
. 函数也是一个对象
. 函数用来保存可执行的代码,且在开发中有需要时可以进行多次调用,我们常使用的print()就是函数。
. 函数中保存的代码不会立即执行,只有当函数调用时,才会执行。
# 语法
def 函数名([参数1,参数2,...])
代码块
接下来简单的写个自定义函数:
2 函数的参数
2.1 参数的分类
我们在定义函数的时候,可以在函数后()中定义若干个形参,参数之间用,隔开
. 形参(形式参数) 定义形参就相当于在函数内部声明了变量,但是并不是赋值
. 实参(实际参数)指定了形参,那么在调用函数时必须传递实参,实参将会赋值给对应的形参,简单来说有几个形参就要有几个实参
# 我们想实现一个简单的功能,求任意两个数的和
def s(a,b):
print(a+b)
s(3,4)
# a,b就是形参,3,4就是实参。一般来说形参和实参**位数**要对等。
2.2 参数的传递方式
定义形参时,可以*为形参指定默认值*。指定了默认值以后,如果用户传递了参数则默认值不会生效。如果用户没有传递,则默认值就会生效
位置参数:位置参数就是将对应位置的实参赋值给对应位置的形参
关键字参数 : 关键字参数可以不按照形参定义的顺序去传递,而根据参数名进行传递
混合使用位置参数和关键字参数的时候必须将位置参数写到关键字参数前面去
. 实参的类型;可以是int,str,fn…;
2.3 不定长参数
定义函数时,可以在形参前面加一个*,这样这个形参可以获取到所有的实参,他会将所有的实参保存到一个元组中 ,即装包
# 求任意个数的和
def sn(*a):
# 定义一个变量来接收结果
res = 0
# 循环遍历a
for i in a:
res += i
print(res)
sn(1,2,3)
带 * 号或 ** 号的形参只能有一个,可以和其他参数配合使用
带 * 形参只能接受*位置参数,
带 ** 接收关键字参数
- 形参不一定只能放在最后,如果不在最后,后面的参数要传关键字传参
形参可以接收其他的关键字参数,它会将这些参数统一保存到字典当中。字典的key就是参数的名字,字典的value就是参数的值
形参只有一个,并且必须写在所有参数的后面
2.4 参数的解包
。传递实参时,也可以在序列类型的参数前添加星号,这样它会自动的将序列中元素依次作为参数传递
。要求序列中的元素的个数必须和形参的个数一致
def fn(a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
t =(10,20,30)
print(*t)
# 执行结果为;10 20 30
# 传递实参时,也可以将序列类型的参数前加*号,这样它会自动的将序列中的每一个元素依次作为参数传递。
# 传递实参时,形参和实参数量要保持一致
def fn(a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
#创建一个字典
d = {'a':1,'b':2,'c':3}
fn(**d)
# 执行结果为
# a = 1
# b = 2
# c = 3
3 函数的返回值
. 返回值就是函数执行以后返回的结果
. 通过return来指定函数的返回值
. return后面可以跟任意对象,返回值甚至可以是一个函数
. 如果仅仅写了return,或者不写return,那么相当于return None
. 在函数中,return和break有些相似之处
# 举例
def fn():
for i in range(5):
print(i)
fn()
# 执行结果为:0 1 2 3 4
# 加入条件判断和break的函数
def fn():
for i in range(5):
if i == 3:
break
print(i)
print('循环执行完毕')
fn()
# 执行结果为:0 1 2 循环执行完毕
# 将break 换成 return后
def fn():
for i in range(5):
if i == 3:
return
print(i)
print('循环执行完毕')
fn()
# 执行结果为0 1 2
。以上可以看出,在函数汇总**return**后面的代码都不执行,即**return**一旦执行,函数自动结束。
4 help()函数及文档字符串Docstring
我们在写代码时,经常会在函数内部编写说明文档,用于说明函数的使用方法和规定,而help(函数名)是可以调用函数的说明文档。
. help()是Python中内置函数,通过help()函数可以查询Python中函数的用法
. 在定义函数时,可以在函数内部编写文档字符串,文档字符串就是对函数的说明
def fn(a,b,c):
"""
这是一个文档字符串的示例
参数
a :作用 类型 默认值 ......
b :作用 类型 默认值 ......
c :作用 类型 默认值 ......
"""
return 100
help(fn)
执行的结果如下:
5 函数作用域(scope)
• 作用域指的是变量生效的区域
a = 10 #函数外部定义全局变量
def fn():
a = 20 # 函数内部定义局部变量
print('函数内部','a=',a)
fn()
print('函数外部', 'a=', a)
执行的结果如下:
• 在Python中一共有两种作用域
• 全局作用域
• 全局作用域在程序执行时创建,在程序执行结束时销毁
• 所有函数以外的区域都是全局作用域
• 在全局作用域中定义的变量,都是全局变量,全局变量可以在程序的任意位置进行访问
• 函数作用域
• 函数作用域在函数调用时创建,在调用结束时销毁
• 函数每调用一次就会产生一个新的函数作用域
• 在函数作用域中定义的变量,都是局部变量,它只能在函数内部被访问
。 那么,如果想让函数内部变量作用到全局,就要用到global 声明,等同于修改全局变量的值,具体如下:
a = 10
def fn():
global a # 在函数内部声明,将局部变量变为全局变量
a = 20
print('函数内部','a=',a)
fn()
print('函数外部', 'a=', a)
加入了global声明后,执行的结果如下:
6 函数命名空间
. 命名空间实际上就是一个字典,是一个专门用来存储变量的字典
locals()用来获取当前作用域的命名空间
. 如果在全局作用域中调用locals()则获取全局命名空间,如果在函. 数作用域中调用locals()则获取函数命名空间
如果在局部函数中想要调用全局命名空间,则调用globals()来获取
. 返回值是一个字典
6.1 在函数外部获取命名空间
a = 10
def fn():
global a
a = 20
print('函数内部','a=',a)
s = locals()#函数外部获取命名空间
print(s)
print(a)
print(s['a'])
执行结果如下:
E:\python练习\venv\Scripts\python.exe E:/python练习/函数命名空间.py
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000001DEC0F0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/python练习/函数命名空间.py', '__cached__': None, 'a': 10, 'fn': <function fn at 0x0000000002823AE8>, 's': {...}}
10
10
Process finished with exit code 0
…那么,利用这个操作可以给函数进行修改
a = 10
def fn():
global a
a = 20
print('函数内部','a=',a)
s = locals()
print(a)
s['c'] = 100
print(c)
print(s)
执行的结果如下:
E:\python练习\venv\Scripts\python.exe E:/python练习/函数命名空间.py
10
100
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000001DEC0F0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/python练习/函数命名空间.py', '__cached__': None, 'a': 10, 'fn': <function fn at 0x0000000001EB3AE8>, 's': {...}, 'c': 100}
Process finished with exit code 0
6.2 在函数内部获取命名空间
def fn4():
s = locals()# 获取函数内部命名空间
print(s)
fn4()
执行结果为
E:\python练习\venv\Scripts\python.exe E:/python练习/函数命名空间.py
{}
Process finished with exit code 0
那么,对函数内容进行填充
def fn4():
a =10
s = locals()# 获取函数内部命名空间
print(s)
fn4()
执行结果如下:
E:\python练习\venv\Scripts\python.exe E:/python练习/函数命名空间.py
{'a': 10}
Process finished with exit code 0
用上面学到的修改函数内容的方式来修改
def fn4():
a =10
s = locals()# 获取函数内部命名空间
s['b'] = 20
print(s)
fn4()
执行结果如下:
E:\python练习\venv\Scripts\python.exe E:/python练习/函数命名空间.py
{'a': 10, 'b': 20}
Process finished with exit code 0
6.3 在内部获取外部函数命名空间
。。 需要用到globals,
def fn4():
a =10
s = locals()# 获取函数内部命名空间
s['b'] = 20
print(s)
fn4()
def fn5():
global_s = globals()
print(global_s)
fn5()
执行的结果如下:
E:\python练习\venv\Scripts\python.exe E:/python练习/函数命名空间.py
{'a': 10, 'b': 20}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000000000214C0F0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/python练习/函数命名空间.py', '__cached__': None, 'fn4': <function fn4 at 0x0000000002833AE8>, 'fn5': <function fn5 at 0x0000000002833A60>}
Process finished with exit code 0
那么,上面学到的非常规修改方法也同样适用,上代码
def fn4():
a = 10
s = locals()# 获取函数内部命名空间
s['b'] = 20
print(s)
fn4()
def fn5():
global_s = globals()
global_s['a'] = 50
print(global_s)
fn5()
print(a)
执行结果如下:
E:\python练习\venv\Scripts\python.exe E:/python练习/函数命名空间.py
{'a': 10, 'b': 20}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000000000275C0F0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'E:/python练习/函数命名空间.py', '__cached__': None, 'fn4': <function fn4 at 0x0000000002823AE8>, 'fn5': <function fn5 at 0x0000000002823A60>, 'a': 50}
50
Process finished with exit code 0
7 递归函数
我们来求 阶乘问题
比如10的阶乘结果为 3628800
方法1 - 最笨的
print(1*2*3*4*5*6*7*8*9*10)
方法2 - 函数
def fn(n):# 定义一个参数接收求阶乘的数
res = n #创建一个变量接收阶乘结果
for i in range(n):
res *= i
return res
print(fn(10))
那么,什么是递归?
• 递归是解决问题的一种方式,它的整体思想,是将一个大问题分解为一个个的小问题,直到问题无法分解时,在去解决问题
•递归式函数有2个条件
• 1. 基线条件 问题可以被分解为最小问题,当满足基线条件时,递归就不执行了
• 2. 递归条件 可以将问题继续分解的条件
def fn2(n):
#基线条件:
if n == 1:
return 1
#递归条件:
return n * fn2(n-1)
print(fn2(10))
8 递归函数小练习
8.1 创建函数,为任意数做任意幂运算
# 创建两个参数
def fn(x,y):
#基线条件
if y == 1:
return x
#递归条件
return x * fn(x,y-1)
print(fn(10,3))
8.2 检查字符串是否是回文字符串
def fn2(s):
# s用来接收检查的字符串
# 递归条件
if len(s) < 2:
return True
elif s[0] != s[-1]:
return False
# 递归条件
return fn2(s[1:-1])
print(fn2(''))
8.3 汉诺塔游戏
现在有ABC三根柱子。要求:将A柱所有的圆盘放到C柱。在移动的过程中可以借助B柱。并且规定大圆盘不能放小圆盘上面,每次只能移动一个盘子。用递归的方式来解决汉诺塔问题。
def hannuoTower(num,a,b,c):
#num 表示盘子
# abcf分表代表A,B,C柱
#基线条件
# 1 如果只有一个盘子,a --> c
if num == 1:
print('第一个盘子从',a,'->',c)
#递归条件
else:
# 2 如果盘子大于2,我们总是可以看成是两个盘子,最下面的和上面的一个或者所有盘子
# 2.1 先把上面的盘子放到b,a --> b
hannuoTower(num-1,a,c,b)
# 2.2 把最下面的盘子放到c,a --> c
print('第',num,'个盘子从',a,'->',c)
# 2.3 把b柱的盘子放到c,b --> c
hannuoTower(num-1,b,a,c)
hannuoTower(3,'A','B','C')
执行结果如下:
E:\python练习\venv\Scripts\python.exe E:/python练习/汉诺塔游戏递归.py
第一个盘子从 A -> C
第 2 个盘子从 A -> B
第一个盘子从 C -> B
第 3 个盘子从 A -> C
第一个盘子从 B -> A
第 2 个盘子从 B -> C
第一个盘子从 A -> C
Process finished with exit code 0
9 高阶函数
9.1 高阶函数的定义
两个特点
. ①接收函数作为参数
. ②将函数作为返回值返回
话不多少,上代码
lst = [1,2,3,4,5,6,7,8,9,10]
# 求列表中的的偶数
# 求列表中大于5的数
# 定义一个函数用来检测偶数
def fn1(n):
if n % 2 == 0:
return True
# 定义一个函数用来检查大于5
def fn2(n):
if n > 5:
return True
def fn(func,lst):#定义参数接收新列表
new_lst = []
for i in lst:
if func(i):
new_lst.append(i)
return new_lst
print(fn(fn2,lst))
当我们使用一个函数作为参数时,实际上是将指定函数中的代码传入了目标函数。
9.2 闭包
•定义 将函数作为返回值也是高阶函数我们也称为闭包
• 闭包的好处:
• 通过闭包可以创建一些只有当前函数能访问的变量
• 可以将一些私有数据藏到闭包中 -> 安全
def fn():
def fn2():
a = 10
print('我是fn2',a)
return fn2
#r 是一个函数,是调用fn()后返回的函数,这个函数实在fn内部定义,并不是全局函数
r = fn()
r()
print(a)
执行的结果如下:(内部可以访问到,而外部访问不到,报错)
E:\phthon资料\venv\Scripts\python.exe E:/phthon资料/闭包.py
我是fn2 10
Traceback (most recent call last):
File "E:/phthon资料/闭包.py", line 10, in <module>
print(a)
NameError: name 'a' is not defined
Process finished with exit code 1
那么,接下来我们做一个小程序来实现计算平均值的功能,上代码:
lst = []
# 定义一个函数来计算平均值
def average(n):
#将n添加到列表中
lst.append(n)
return(sum(lst)/len(lst))
print(average(10))
print(average(20))
lst.append('python')
print(average(10))
执行结果如下:
E:\phthon资料\venv\Scripts\python.exe E:/phthon资料/闭包.py
10.0
15.0
Traceback (most recent call last):
File "E:/phthon资料/闭包.py", line 21, in <module>
print(average(10))
File "E:/phthon资料/闭包.py", line 17, in average
return(sum(lst)/len(lst))
TypeError: unsupported operand type(s) for +: 'int' and 'str'
Process finished with exit code 1
我们能清楚的看到,由于代码中的变量lst为全局变量,在外部也可以访问到,那么就很容易被他人修改,这就是为什么要闭包,那么怎么修改呢?上代码
def make_average():
lst = []
# 定义一个函数来计算平均值
def average(n):
#将n添加到列表中
lst.append(n)
return(sum(lst)/len(lst))
return average
a = make_average()
print(a(10))
执行结果如下:
E:\phthon资料\venv\Scripts\python.exe E:/phthon资料/闭包.py
10.0
15.0
Process finished with exit code 0
• 行成闭包的条件
• 函数嵌套
• 将内部函数作为返回值返回
• 内部函数必须要使用到外部函数的变量
9.3装饰器的引入
上代码:
# 求任意两个数的和
def add(a,b):
return a + b
# 求任意两个数的积
def mul(a,b):
return a * b
r = add(1,3)
print(r)
那么,如果我们想在代码执行前打印‘开始计算’,结束后打印‘结束结算’呢
def add(a,b):
print('开始计算')
print('结束计算')
return a + b
# 求任意两个数的积
def mul(a,b):
return a * b
r = add(1,3)
print(r)
那么,如果想在所有的函数中都实现这个功能呢,那么依靠这种本办法挨个修改添加显然是行不通的
• 我们可以直接通过修改函数中的代码来完成需求,但是会产生以下一些问题
• 如果修改的函数多,修改起来会比较麻烦
• 不方便后期的维护
• 这样做会违反开闭原则(ocp即Open-Close Principle),即程序的设计,要求开放对程序的扩展,要关闭对程序的修改
9.4 装饰器的使用
. 通过装饰器,可以在不修改原来函数的情况下来对函数进行扩展
. 在开发中,我们都是通过装饰器来扩展函数的功能的
上代码
# 求任意两个数的和
def add(a,b):
return a + b
# 求任意两个数的积
def mul(a,b):
return a * b
def star_end(old):
#定义一个函数来对其他的函数进行扩展,扩展函数执行的时候,打印'开始计算',执行后打印‘结束计算’
#参数old来接收要拓展的参数
def new_function(*args,**kwargs):
print('开始计算')
res = old(*args,**kwargs)
print('结束计算')
return res
#返回新函数
return new_function
f = star_end(add)#此处需要修改哪个函数,就把函数名传入即可
r = f(66,55)
print(r)
那么,也可以这样写
# 求任意两个数的和
def add(a,b):
return a + b
# 求任意两个数的积
def mul(a,b):
return a * b
def star_end(old):
#定义一个函数来对其他的函数进行扩展,扩展函数执行的时候,打印'开始计算',执行后打印‘结束计算’
#参数old来接收要拓展的参数
def new_function(*args,**kwargs):
print('开始计算')
res = old(*args,**kwargs)
print('结束计算')
return res
#返回新函数
return new_function
f = star_end(add)#此处需要修改哪个函数,就把函数名传入即可
@star_end
def speak():
print('大家加油')
speak()
执行结果如下:
E:\phthon资料\venv\Scripts\python.exe E:/phthon资料/装饰器.py
开始计算
大家加油
结束计算
Process finished with exit code 0