函数也是一个对象。函数可以用来保存一些可执行的代码块,实现多次调用
1. 创建函数
def 函数名([形参1, 形参2, 形参3, ..., 形参n]):
函数名可以包含数字,字母,下划线,汉字但是不能以数字开头
def test_01():
print('这是 test_01 函数')
# 调用
test_01()
>>>结果:
这是 test_01 函数
================================
print(type(test_01), id(test_01))
>>>结果:
<class 'function'> 1879503552032
===============================
# 定义一个函数,求两个数的和
def num_sum(num1, num2) :
print(num1 + num2)
num_sum(1,2)
>>>结果:
3
==================================
print(函数名(**param)) # 这是打印函数的返回值
print(函数名) # 这是打印函数的对象
>>>结果:
返回值
<function 函数名 at 0x000001E6A0A03E20>
2. 参数
定义函数时,可以指定形参,可以是多个形参,也可以没有形参,形参使用 ,
隔开。
形参:形式参数。定义形参相当于在函数内部声明了变量,但是不赋值,在创建函数的时候声明
指定形参时,可以为形参执行默认值,如果调用时传递了该参数,则该默认值不生效,如果没有传递该参数,默认值就生效了。
def test_02(a, b = 2, c = 3) : print(a,b,c) test_02(5) >>>结果: 5 2 3
在定义函数时,可以在形参前面加一个星号,这样这个形参会获得所有实参(称之为参数的装包)
def test_03(*a) : print('a = ',a,'\t type:' , type(a)) test_03() >>>结果: a = () type: <class 'tuple'> ===================================================== def test_03(*a) : result = 0 for i in a : result += i print(result) test_03(1,2,3,4,5,6,7,8) >>>结果: 36
注意事项:带
*
的参数只能有一个,并且可以和其他参数配合使用,在传递其他参数时,必须以关键字的形式传递实参。如果在参数最前面写了一个*
,表示所有的参数都必须使用关键字传递实参,并且 ·*a
只接收位置参数,不接收关键字参数。**a
可以接收其他关键字参数,他会将参数存储到一个字典中,key 是参数名称,value 是参数的值。**a
只能有一个,并且只能写在所有函数的最后面def test_03(*a,b,c) : result = 0 for i in a : result += i print(result,b,c) test_03(1,2,3,4,5,6,b = 7,c = 8) >>>结果: 21 7 8 ========================================================== def test_03(**a) : print(a,type(a)) test_03(a=1,b=2,c=3,d=4) >>>结果: {'a': 1, 'b': 2, 'c': 3, 'd': 4} <class 'dict'> ========================================================== def test_03(e,f,**a) : print(a,type(a),e,f) test_03(1,2,a=1,b=2,c=3,d=4) >>>结果: {'a': 1, 'b': 2, 'c': 3, 'd': 4} <class 'dict'> 1 2
实参:实际参数。实参是给形参赋值的,在调用的时候传递,实参可以传递任意类型的参数。在调用函数时,解释器不会检查实参的类型。
位置参数:就是将对应位置的实参赋值给对应位置的形参,第一个实参赋值给第一个形参,以此类推。这是我们正常使用的
关键字参数:可以不按照形参定义的顺序传递,而是根据参数名称去传递参数
def test_02(a, b = 2, c = 3) : print(a,b,c) test_02(c =5,b =10,a = 23) >>>结果: 23 10 5
位置参数可以跟关键字参数混合使用,必须将位置参数写到前面
def test_02(a, b = 2, c = 3) : print(a,b,c) test_02(45, c = 34) >>>结果: 45 2 34
当参数是一个对象的时候,如果在函数内部修改了对象的值,那么也会影响函数外面的变量
参数的解包
传递实参时,也可以在序列类型的参数前面添加
*
,这样就可以自动将序列中的元素依次作为参数传递,要求序列中元素的个数必须跟参数的个数保持一致。def test_04(a, b, c) : print(a,b,c) param = (10,20,30) # 这里可以是元组,列表等 test_04(*param) >>>结果: 10 20 30
也可以使用
**
给字典解包def test_04(a, b, c) : print(a,b,c) param = {'a' : 10, 'b' : 20, 'c' : 30} test_04(**param) >>>结果: 10 20 30
3. 返回值
返回值就是函数执行结束之后的结果。可以通过return
指定返回值。
如果没有写 return 或者仅仅写了一个 return ,相当于 return None
def test_04(a, b, c) :
return a + b +c
param = {'a' : 10, 'b' : 20, 'c' : 30}
result = test_04(**param)
print(result)
>>>结果:
60
在函数中,return 后面的代码都不会执行,函数会自动结束
4. 文档字符串(doc str)
在定义函数时,可以在函数内部编写文档字符串,文档字符串就是函数的使用说明。可以通过help()
来查看函数的使用说明
在函数内部的第一行书写一个字符串就是文档字符串。
def test_04(a, b, c) :
'''
这是一个测试函数。
a, 类型,默认值,作用等等
a, 类型,默认值,作用等等
a, 类型,默认值,作用等等
'''
return a + b +c
help(test_04)
>>>结果:
Help on function test_04 in module __main__:
test_04(a, b, c)
这是一个测试函数。
a, 类型,默认值,作用等等
a, 类型,默认值,作用等等
a, 类型,默认值,作用等等
================================================
def test_04(a:int, b:int, c:int) -> int:
'''
这是一个测试函数。
a, 类型,默认值,作用等等
a, 类型,默认值,作用等等
a, 类型,默认值,作用等等
'''
return a + b +c
# 这个说明了 参数都是int 类型, ->int 表示 返回值为int类型
5. 作用域(scope)
指的是变量生效的区域。
一共有两种作用域
全局作用域:在程序执行时创建,在程序结束时销毁,所有函数以外的区域都是全局作用域,变量叫做全局变量,可以在程序任意位置访问
函数作用域: 在函数调用时创建,在函数结束时销毁。函数每被调用一次,就会产生新的函数作用域,在函数作用域中定义的变量,都是局部变量,只能在函数内部被访问。
使用变量时,会优先在当前作用域中寻找该变量,如果有则使用,如果没有,则继续去上一级作用域中寻找,以此类推。
如果希望在函数内部修改全局变量,则需要使用
global
关键字声明变量,然后修改就会修改全局变量。d = 10 def test_04(a:int, b:int, c:int) -> int: ''' 这是一个测试函数。 a, 类型,默认值,作用等等 a, 类型,默认值,作用等等 a, 类型,默认值,作用等等 ''' d = 20 print(d) return a + b +c test_04(1,2,3) print(d) >>>结果: 20 10 # 表示没有修改全局变量,输出的还是 10 =================================================== d = 10 def test_04(a:int, b:int, c:int) -> int: ''' 这是一个测试函数。 a, 类型,默认值,作用等等 a, 类型,默认值,作用等等 a, 类型,默认值,作用等等 ''' global d # 使用global 生命变量,就可以修改全局变量了 d = 20 print(d) return a + b +c test_04(1,2,3) print(d) >>>结果: 20 20 # 表示修改全局变量,输出是修改之后的值
6. 命令空间(namespace)
命令空间指的是变量存储的位置,每一个变量都需要存储到指定的命名空间中,每一个作用域都有一个对应的命名空间
全局命名空间保存全局变量,函数命名空间保存局部变量
命名空间其实就是一个字典,是一个专门存储对象的字典
使用locals()
函数获取当前作用域的命名空间,返回的是一个字典
如果在全局作用域中执行locals()
,则获取全局命名空间,如果在函数作用域中执行locals()
,则获取函数命名空间
d = 10
def test_04(a:int, b:int, c:int) -> int:
'''
这是一个测试函数。
a, 类型,默认值,作用等等
a, 类型,默认值,作用等等
a, 类型,默认值,作用等等
'''
global d
d = 20
print(d)
return a + b +c
scope = locals()
print(scope)
>>>结果:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001D38BE54A90>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'XXX.py', '__cached__': None, 'd': 20, 'test_04': <function test_04 at 0x000001D38BD93E20>, 'scope': {...}}
通过操作locals()
的返回值,可以获取变量或者添加变量(一般不建议这么操作)
scope = locals()
print(scope['d'])
scope['aa'] = 20
print(scope['aa'])
>>>结果:
10
20
可是使用global()
函数,在任意位置获取全局命名空间。
7. 递归
递归式函数就是在函数中自己调自己
求 10 的阶乘
# 求 10 的阶乘
def test_05(n) :
result = n
for i in range(1, n) :
result *= i
return result
print(test_05(10))
>>>结果:
3628800
===========================================
#使用递归
# 10! = 10 * 9!
# 9! = 9 * 8!
# 8! = 8 * 7!
# ...
# 2! = 2 * 1!
# 1! = 1
def test_05(n) :
if n == 1:
return 1
return n * test_05(n -1)
print(test_05(10))
>>>结果:
3628800
============================================
# 求 n 的 i 次幂
def test_06(n, i) :
if i == 1:
return n
return n * test_06(n, i -1)
print(test_06(2,8))
>>>结果:
256
============================================
# 检查是否为回文
def hui_wen(param_str:str) :
if len(param_str) < 2 :
return True
elif param_str[0] != param_str[-1] :
return False
return hui_wen(param_str[1 : -1])
print(hui_wen('abcba'))
>>>结果:
True
8. 高阶函数
函数式编程,在 Python 中,函数是一等对象
一等对象有以下特点:
对象是在运行时创建
能赋值给变量或作为数据结构中的元素
能作为参数传递
能作为返回值
高阶函数:接收函数作为参数,或者将函数作为返回值的函数就是高阶函数
接收一个或者多个函数作为参数
将函数作为返回值返回
当使用一个函数作为参数时,实际上是将指定的代码传递进了目标函数
def fn2(i) : ''' 检查是否为偶数 ''' if i % 2 == 0 : return True return False def fn3(i) : ''' 检查是否为奇数 ''' if i % 2 != 0 : return True return False def fn(func, n) : result_list = [] for i in n : if func(i) : result_list.append(i) return result_list li = [1,2,3,4,5,6,7,8,9,10] # 这里将函数作为参数进行传递 print(fn(fn2, li)) >>>结果: [2, 4, 6, 8, 10] # 这里传参了函数 fn2
9. 匿名函数
filter(function,list)
过滤器。可以从序列中过滤出符合条件的数据,保存到一个新的序列中
参数
函数,根据该函数来过滤序列(可迭代的结构)
需要过滤的序列(可迭代的结构)
返回值:过滤后的新序列
def fn2(i) : ''' 检查是否为偶数 ''' if i % 2 == 0 : return True return False def fn3(i) : ''' 检查是否为奇数 ''' if i % 2 != 0 : return True return False li = [1,2,3,4,5,6,7,8,9,10] print(filter(fn2, li)) print(list(filter(fn2, li))) >>>结果: <filter object at 0x0000024D4A5FBC40> [2, 4, 6, 8, 10]
上面的不推荐使用,建议使用匿名函数
lambda表达式
用来创建一些简单函数
语法: lambda 参数列表 : 返回值
lambda a,b : a + b
调用 (lambda a,b : a + b)(10,20)
# 声明
lambda a, b : a + b
# 调用
print((lambda a, b : a + b)(10,29))
>>>结果:
39
============================================
# 声明
fn5 = lambda a, b : a + b
# 调用
print(fn5(10 ,20))
>>>结果:
30
============================================
li = [1,2,3,4,5,6,7,8,9,10]
print(list(filter(lambda n : n % 2 == 0,li)))
>>>结果:
[2, 4, 6, 8, 10]
map(function,list)
:map 函数可以对可迭代对象中的元素做指定的操作,然后将其添加到一个新的对象中返回li = [1,2,3,4,5,6,7,8,9,10] print(list(map(lambda n : n + 1,li))) >>>结果: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
匿名函数一般作为参数使用,其他地方一般不使用。
sort()
函数时排序函数。默认是使用 < 比较的。sort
可以接收一个关键字参数,key 需要函数作为参数,当设置了此函数,每次都会以列表中的一个元素作为参数来调用此函数,并且使用函数的返回值来比较元素的大小li1 = ['sd', 'sdfsdfs', 'aaa', 'wsdrsdfsdfsdfsdf'] li1.sort(key = len, reverse = True) print(li1) >>>结果: ['wsdrsdfsdfsdfsdf', 'sdfsdfs', 'aaa', 'sd']
sorted()
用法跟sort()
基本一致,但是sorted()
可以对任意序列进行排序,并且不会影响原来的对象,而是返回一个新的对象
10. 闭包
将函数作为返回值返回
def fn1() :
nums_list = []
def fn2(n) :
nums_list.append(n)
return sum(nums_list)/len(nums_list)
return fn2
l = fn1()
print(l(4))
print(l(6))
print(l(9))
print(l(10))
>>>结果:
4.0
5.0
6.333333333333333
7.25
11. 装饰器
下面的 fn
函数就是个装饰器,可以直接调用,也可以使用 @装饰器
进行使用,装饰器可使用多个
def fn(old):
'''
用来对其他函数进行扩展
参数:
old: 要扩展的函数对象
'''
def fn2(*args, **kwargs):
print('函数执行开始....')
result = old(*args, **kwargs)
print('函数执行结束....')
return result
return fn2
def add(a, b):
return a + b
def say_hello():
print('大家好...')
r = fn(add)
print(r)
c = r(2,4)
print(c)
r= fn(say_hello)
print(r)
print(r())
>>>结果:
<function fn.<locals>.fn2 at 0x000001D1AD21A170>
函数执行开始....
函数执行结束....
6
<function fn.<locals>.fn2 at 0x000001D1AD21A200>
函数执行开始....
大家好...
函数执行结束....
None
==============================================================
# 可以直接使用这个扩展函数
@fn
def fn3(a, b):
return a * b
print(fn3(2,4))
>>>结果:
函数执行开始....
函数执行结束....
8