本文通过理论+代码的方式介绍了Python函数的相关知识:包括:函数的参数、参数传递方式、参数解包、返回值、文档字符串、作用域、命名空间、递归、高阶函数、匿名函数、闭包、装饰器。在开发过程中或者看别人源码的过程中,这些知识是非常有用和经常遇到的,因此掌握这些知识很重要。本文也是Python系列文章的第3篇。
【Python 基础】一文补齐Python基础知识
【趣学Python:B站四大恶人】一文掌握列表、元组、字典、集合
【Python进阶】一文掌握Python函数用法
【Python进阶】Python面向对象之类与对象详解
【Python进阶】Python面向对象之装饰器与封装详解
【Python进阶】Python面向对象之继承和多态详解
【Python进阶】Python异常处理和模块详解
【Python进阶】Python文件(I/O)操作详解
fn
是函数对象 ;fn()
调用函数;例如:print
是函数对象 print()
调用函数;def 函数名([形参1,形参2,...形参n]) :
代码块
- 函数名必须要符号标识符的规范;
(可以包含字母、数字、下划线、但是不能以数字开头)
- 函数中保存的代码不会立即执行,需要调用函数代码才会执行;
函数对象()
在定义函数时,可以在函数名后的()中定义数量不等的形参,多个形参之间使用,
隔开;
# 定义函数时,指定形参
def fn(a, b) :
print(a,"+",b,"=",a + b)
# 调用函数时,传递实参
fn(10,20)
fn(123,456)
实参的传递方式:
def mul(a,b,c):
print(a*b*c)
fn(1 , 2 , 3)
;fn(b=1 , c=2 , a=3)
;fn(1,c=30)
;def fn4(a):
a[0] = 30
print('a =',a,id(a))
c = 10
c = [1,2,3]
fn4(c)
# 不改变对象
fn4(c.copy()) # or fn4(c[:])
# 定义一个函数,可以求任意个数字的和
def sum(*nums):
# 定义一个变量,来保存结果
result = 0
# 遍历元组,并将元组中的数进行累加
for n in nums :
result += n
print(result)
*
,这样这个形参将会获取到所有的实参;它将会将所有的实参保存到一个元组中。*a
会接受所有的位置实参,并且会将这些实参统一保存到一个元组中(装包)def fn(*a):
print("a =",a,type(a))
fn()
'''
输出:
a = ()
'''
def fn2(a,b,*c):
print('a =',a)
print('b =',b)
print('c =',c)
fn2(1,2,3,4,5,6)
'''
输出:
a = 1
b = 2
c = (3, 4, 5, 6)
'''
*
的参数后的所有参数,必须以关键字参数的形式传递;第一个参数给a,剩下的位置参数给b的元组,c必须使用关键字参数;def fn3(a,*b,c):
print('a =',a)
print('b =',b)
print('c =',c)
fn3(1,2,3,4,5,c=6)
'''
输出:
a = 1
b = (2, 3, 4, 5)
c = 6
'''
def fn4(*a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
fn4(1,2,3,4,b=5,c=6)
'''
输出:
a = (1, 2, 3, 4)
b = 5
c = 6
'''
*
,则要求所有的参数必须以关键字参数的形式传递;def fn5(*,a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
fn5(a=4,b=5,c=6)
'''
输出:
a = 4
b = 5
c = 6
'''
*
形参只能接收位置参数,而不能接收关键字参数;**
形参可以接收其他的关键字参数,它会将这些参数统一保存到一个字典中;字典的key就是参数的名字,字典的value就是参数的值;**
形参只能有一个,并且必须写在所有参数的最后;def fn6(b,c,**a) :
print('a =',a,type(a))
print('b =',b)
print('c =',c)
fn6(b=1,d=2,c=3,e=10,f=20)
'''
输出:
a = {'d': 2, 'e': 10, 'f': 20}
b = 1
c = 3
'''
def fn7(a,b,c):
print('a =',a)
print('b =',b)
print('c =',c)
# 创建一个元组
t = (10,20,30)
fn7(t[0],t[1],t[2])
# 创建一个列表
s = [10,20,30]
fn7(*s)
# 创建一个字典
d = {'a':10,'b':20,'c':30}
fn7(**d)
'''
输出:
a = 10
b = 20
c = 30
'''
def fn():
def fn2() :
print('hello')
return fn2 # 返回值也可以是一个函数
r = fn() # 这个函数的执行结果就是它的返回值
break
用来退出当前循环;continue
用来跳过当次循环;return
用来结束函数;def fn4() :
for i in range(5):
if i == 3 :
# break 用来退出当前循环
# continue 用来跳过当次循环
return # return 用来结束函数
print(i)
print('循环执行完毕!')
'''
break执行结果:0,1,2,循环执行完毕!
continue执行结果:0,1,2,4,循环执行完毕!
return执行结果:0,1,2
'''
def fn2() :
a = 10
return
def sum(*nums):
# 定义一个变量,来保存结果
result = 0
# 遍历元组,并将元组中的数进行累加
for n in nums :
result += n
return result
r = sum(1,2,3)
print(r)
print(r + 4)
def fn5():
return 10
fn5
和 fn5()
的区别:
print(fn5)
输出print(fn5())
输出10;help()
是Python中的内置函数;
通过help()
函数可以查询python中的函数的用法;
语法:help(函数对象)
;
help(print)
获取print()函数的使用说明;
文档字符串(doc str);
在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的说明;
当我们编写了文档字符串时,就可以通过 help()
函数来查看函数的说明;
文档字符串非常简单,其实直接在函数的第一行写一个字符串就是文档字符串;
-> int
表示函数返回值是int
类型;
def fn(a:int,b:bool,c:str='hello') -> int:
'''
这是一个文档字符串的示例
函数的作用:。。。。。
函数的参数:
a,作用,类型,默认值。。。。
b,作用,类型,默认值。。。。
c,作用,类型,默认值。。。。
'''
return 10
help(fn)
全局作用域
函数作用域
变量的查找
NameError: name 'a' is not defined
;def fn2():
a = 10
def fn3():
print('fn3中:','a =',a)
fn3()
fn2()
'''
输出:
10
'''
在函数中为变量赋值时,默认都是为局部变量赋值;如果希望在函数内部修改全局变量,则需要使用 global
关键字,来声明变量;
def fn():
global a # 声明在函数内部的使用a是全局变量,此时再去修改a时,就是在修改全局的a
a = 10 # 修改全局变量
print('函数内部:','a =',a)
fn()
print('函数外部:','a =',a)
命名空间(namespace)
locals()
用来获取当前作用域的命名空间
locals()
则获取全局命名空间;如果在函数作用域中调用locals()
则获取函数命名空间;返回的是一个字典;scope = locals()
获取当前命名空间;def fn4():
a = 10
# scope = locals() # 在函数内部调用locals()会获取到函数的命名空间
# scope['b'] = 20 # 可以通过scope来操作函数的命名空间,但是也是不建议这么做
# globals() 函数可以用来在任意位置获取全局命名空间
global_scope = globals()
# print(global_scope['a'])
global_scope['a'] = 30
# print(scope)
fn4()
# 尝试求10的阶乘(10!)
创建一个变量保存结果
n = 10
for i in range(1,10):
n *= i
print(n)
创建一个函数,可以用来求任意数的阶乘:
def factorial(n):
'''
该函数用来求任意数的阶乘
参数:n 要求阶乘的数字
'''
# 创建一个变量,来保存结果
result = n
for i in range(1,n):
result *= i
return result
递归式的函数
def fn():
fn()
fn()
递归式函数的两个要件:
1.基线条件
2.递归条件
使用递归重写计算任意阶乘的函数:
def factorial(n):
'''
该函数用来求任意数的阶乘
参数:n 要求阶乘的数字
'''
# 基线条件;判断n是否为1,如果为1则此时不能再继续递归
if n == 1 :
# 1的阶乘就是1,直接返回1
return 1
# 递归条件
return n * factorial(n-1)
print(factorial(10))
'''
练习
创建一个函数 power 来为任意数字做幂运算 n ** i
10 ** 5 = 10 * 10 ** 4
10 ** 4 = 10 * 10 ** 3
...
10 ** 1 = 10
'''
def power(n , i):
'''
power()用来为任意的数字做幂运算
参数:
n 要做幂运算的数字
i 做幂运算的次数
'''
# 基线条件
if i == 1:
# 求1次幂
return n
# 递归条件
return n * power(n , i-1)
print(power(8,6))
print(8**6)
创建一个函数,用来检查一个任意的字符串是否是回文字符串,如果是返回True,否则返回False。
'''
回文字符串,字符串从前往后念和从后往前念是一样的
abcba
abcdefgfedcba
先检查第一个字符和最后一个字符是否一致,如果不一致则不是回文字符串
如果一致,则看剩余的部分是否是回文字符串
检查 abcdefgfedcba 是不是回文
检查 bcdefgfedcb 是不是回文
检查 cdefgfedc 是不是回文
检查 defgfed 是不是回文
检查 efgfe 是不是回文
检查 fgf 是不是回文
检查 g 是不是回文
'''
def hui_wen(s):
'''
该函数用来检查指定的字符串是否回文字符串,如果是返回True,否则返回False
参数:
s:就是要检查的字符串
'''
# 基线条件
if len(s) < 2 :
# 字符串的长度小于2,则字符串一定是回文
return True
elif s[0] != s[-1]:
# 第一个字符和最后一个字符不相等,不是回文字符串
return False
# 递归条件
return hui_wen(s[1:-1])
print(hui_wen('abcdefgfedcba'))
# 创建一个列表
l = [1,2,3,4,5,6,7,8,9,10]
# 定义一个函数,用来检查一个任意的数字是否是偶数
def fn2(i) :
if i % 2 == 0 :
return True
return False
# 这个函数用来检查指定的数字是否大于5
def fn3(i):
if i > 5 :
return True
return False
def fn(func , lst) :
'''
fn()函数可以将指定列表中的所有偶数获取出来,并保存到一个新列表中返回
参数:
lst:要进行筛选的列表
'''
# 创建一个新列表
new_list = []
# 对列表进行筛选
for n in lst :
# 判断n的奇偶
if func(n) :
new_list.append(n)
# 返回新列表
return new_list
def fn4(i):
return i % 3 == 0
print(fn(fn4 , l))
filter()
可以从序列中过滤出符合条件的元素,保存到一个新的序列中r = filter(fn4, 1)
print(r)
fn4
是作为参数传递进filter()
函数中;fn4实际上只有一个作用,就是作为filter()
的参数;filter()
调用完毕以后,fn4
就已经没用;lambda 参数列表 : 返回值
;r = filter(lambda i : i > 5 , l)
print(list(r))
map()
函数可以对可跌倒对象中的所有元素做指定的操作,然后将其添加到一个新的对象中返回;l = [1,2,3,4,5,6,7,8,9,10]
r = map(lambda i : i ** 2 , l)
print(list(r))
sort()
方法默认是直接比较列表中的元素的大小;sort()
可以接收一个关键字参数key=
,key=
需要一个函数作为参数,当设置了函数作为参数,每次都会以列表中的一个元素作为参数来调用函数,并且使用函数的返回值来比较元素的大小。l = ['bb','aaaa','c','ddddddddd','fff']
l.sort(key=len)
print(l)
# 输出:['c', 'bb', 'fff', 'aaaa', 'ddddddddd']
l = [2,5,'1',3,'6','4']
l.sort(key=int)
print(l)
# 输出:['1', 2, 3, '4', 5, '6']
sort()
的用法基本一致,但是sorted()可以对任意的序列进行排序;l = [2,5,'1',3,'6','4']
print('排序前:',l)
print(sorted(l,key=int))
print('排序后:',l)
'''
输出:
排序前: [2, 5, '1', 3, '6', '4']
['1', 2, 3, '4', 5, '6']
排序后: [2, 5, '1', 3, '6', '4']
'''
def fn():
a = 10
# 函数内部再定义一个函数
def inner():
print('我是fn2' , a)
# 将内部函数 inner作为返回值返回
return inner
r = fn()
r
是一个函数,是调用fn()
后返回的函数;fn()
内部定义,并不是全局函数;fn()
函数内的变量;形成闭包的要件:
① 函数嵌套;
② 将内部函数作为返回值返回;
③ 内部函数必须要使用到外部函数的变量;
def make_averager():
# 创建一个列表,用来保存数值
nums = []
# 创建一个函数,用来计算平均值
def averager(n) :
# 将n添加到列表中
nums.append(n)
# 求平均值
return sum(nums)/len(nums)
return averager
averager = make_averager()
print(averager(10)) # 输出:10.0
print(averager(20)) # 输出:15.0
print(averager(30)) # 输出:20.0
print(averager(40)) # 输出:25.0
# 创建几个函数
def fn():
print('我是fn函数....')
def add(a , b):
'''
求任意两个数的和
'''
r = a + b
return r
def mul(a , b):
'''
求任意两个数的积
'''
r = a * b
return r
需求:希望函数可以在计算前,打印开始计算,计算结束后打印计算完毕。
我们可以直接通过修改函数中的代码来完成这个需求,但是会产生以下一些问题:
1.如果要修改的函数过多,修改起来会比较麻烦;
2.并且不方便后期的维护;
3.并且这样做会违反开闭原则(OCP):
程序的设计,要求开发对程序的扩展,要关闭对程序的修改;
为了解决这个问题,我们创建一个函数,让这个函数可以自动的帮助我们生产函数;
def begin_end(old):
'''
用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束
参数:
old 要扩展的函数对象
'''
# 创建一个新函数
def new_function(*args , **kwargs):
print('开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs)
print('执行结束~~~~')
# 返回函数的执行结果
return result
# 返回新函数
return new_function
*args
接收所有位置参数;**args
接收所有的关键字参数;def new_function(*args , **kwargs)
中的两个参数是把传入的参数装包成一个元组(*args
)或一个字典(**kwargs
);result = old(*args , **kwargs)
中的两个参数,*args
将元组拆包成位置参数传入函数,**kwargs
将字典拆包成关键字参数传入函数。调用:
f = begin_end(fn)
f2 = begin_end(add)
f3 = begin_end(mul)
r = f()
print('第一次输出:\n',r)
r = f2(123,456)
print('第二次输出:\n',r)
r = f3(123,456)
print('第三次输出:\n',r)
'''
输出:
开始执行~~~~
我是fn函数....
执行结束~~~~
第一次输出:
None
开始执行~~~~
执行结束~~~~
第二次输出:
579
开始执行~~~~
执行结束~~~~
第三次输出:
56088
'''
像 begin_end()
这种函数我们就称它为装饰器;
@装饰器
,来使用指定的装饰器,来装饰当前的函数;装饰器的标准用法:
@begin_end
def say_hello():
print('大家好~~~')
say_hello()
'''
输出:
大家好~~~
'''
def fn3(old):
'''
用来对其他函数进行扩展,使其他函数可以在执行前打印开始执行,执行后打印执行结束
参数:
old 要扩展的函数对象
'''
# 创建一个新函数
def new_function(*args , **kwargs):
print('fn3装饰~开始执行~~~~')
# 调用被扩展的函数
result = old(*args , **kwargs)
print('fn3装饰~执行结束~~~~')
# 返回函数的执行结果
return result
# 返回新函数
return new_function
@fn3
@begin_end
def say_hello():
print('大家好~~~')
say_hello()
'''
输出:
fn3装饰~开始执行~~~~
开始执行~~~~
大家好~~~
执行结束~~~~
fn3装饰~执行结束~~~~
'''
交换装饰器执行顺序
@begin_end
@fn3
def say_hello():
print('大家好~~~')
say_hello()
'''
输出:
开始执行~~~~
fn3装饰~开始执行~~~~
大家好~~~
fn3装饰~执行结束~~~~
执行结束~~~~
'''
参考课程:https://edu.aliyun.com/course/1782