函数简介(function)
python函数和java不同,python定义函数时参数不需要类型,返回值也不需要类型
- 函数也是一个对象
- 对象是内存中专门用来存储数据的一块区域
- 函数也可用来保存很多可执行的代码,可以多次调用
- 创建函数:def 函数名(参数,参数,…):代码块
- 函数名() 和 函数名 区别
函数名() 是执行函数 或者说是 调用函数
函数名 是函数对象,可作为参数传递 - 函数在调用时创建,调用结束后销毁
函数参数
- 函数参数可有可无,根据需求而定
- 函数参数可指定默认值 def 函数名([参数,参数=‘000’]):代码块
- 关键字参数:可以不按照顺序传递参数,而直接根据参数名传递
def test(a,b,c): pass #关键字参参数 test(b=1,c=2,a=3)
- 实参可以是任意类型,在函数中要检验数据类型
不定长参数
- 可以在形参前加上*,这个参数就会接收所有实参以元组类型体现 称之为装包
def test(*a): pass #不定个数参数 test(1,2,3,4)
- 带*号的形参不在最后时,它后边的的参数都应以关键字参数形式传递
def test(*a,b,c): pass # 带*号的形参不在最后时 test(1,2,3,4,5,6,b=7,c=8)
- 带*号的形参不能接收关键字参数
- 关键字参数可以使用**形参保存为字典,必须在最后
def test(**a): pass # **形参 test(a=6,b=7,c=8)
参数解包
- 解包序列参数个数要符合函数接收参数个数
def test(a,b,c): pass // 定义一个序列 s = (1,2,3) // 直接传s会报错,可以解包传参 test(*s)
- 一个*是对元组,集合,列表解包,两个**是对字典解包
文档字符串
- 在python中,可以使用help()内置函数查看函数的使用说明,如:
打印结果# 查看函数说明时 函数要以对象方式传入,不能加() help(print)
这个说明就是文档字符串说明Help on built-in function print in module builtins: print(...) print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) Prints the values to a stream, or to sys.stdout by default. Optional keyword arguments: file: a file-like object (stream); defaults to the current sys.stdout. sep: string inserted between values, default a space. end: string appended after the last value, default a newline. flush: whether to forcibly flush the stream.
- 编写文档字符串
打印结果# 文档字符串非常简单,就是用三个单引号包括上 def text(a,b,c): ''' 你好,我是一个测试函数的文档字符串 ''' print(a)
Help on function text in module __main__: text(a, b, c) 你好,我是一个测试函数的文档字符串
- 编写文档字符串,并对入参出参做说明
打印结果# 文档字符串非常简单,就是用三个单引号包括上 # 入参出参说明 只是说明,不会强制入参出参类型 # 解读text函数:入参为 整数类型a,字符串类型b,布尔值类型c;返回值是字符串类型 def text(a:int,b:str,c:bool) -> str: ''' 你好,我是一个测试函数的文档字符串 ''' print(a)
Help on function text in module __main__: text(a: int, b: str, c: bool) -> str 你好,我是一个测试函数的文档字符串 ***Repl Closed***
作用域
- 变量默认回在自己的作用域中使用,
- 默认形况下函数中是不能修改全局变量的如果想修改,则使用关键字 global
a = 10 def test(): global a # 声明在函数中使用的是全局变量a a = 20 # 此时就把全局变量a的值改了
- 函数嵌套,想修改上一级函数中变量,则使用关键字 nonlocal
如果在函数内不用global声明你个,则函数内变量a只是和全局a同名,但并不是一个def make_test(): a = 10 def test(): nonlocal a # 声明在函数中使用的是上一级函数中变量a a = 20 # 此时就可以修改变量a的值了
命名空间
- 命名空间实际上就是一个字典,专门存储当前作用域中的变量、函数
- 使用 locals 来获取当前作用域的命名空间
打印结果# 定义一些变量和函数 以作测试 a = 10 b = 20 c = 'hello' d = True e = 5.66 def test_a(): pass def test_b(): return 10 scope = locals() print(scope)
{ '__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x003BAF40>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'testb.py', '__cached__': None, 'a': 10, 'b': 20, 'c': 'hello', 'd': True, 'e': 5.66, 'test_a': <function test_a at 0x01188190>, 'test_b': <function test_b at 0x01188148>, 'scope': {...} }
- locals就是一个字典,也就是说我们可以直接操作字典添加变量(一般不建议这样做)
打印结果# 向命名空间添加一个列表变量f scope['f'] = ['a','b','c']
{ '__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0132AF40>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'testb.py', '__cached__': None, 'a': 10, 'b': 20, 'c': 'hello', 'd': True, 'e': 5.66, 'test_a': <function test_a at 0x013B8190>, 'test_b': <function test_b at 0x013B8148>, 'scope': {...}, 'f': ['a', 'b', 'c'] }
- 也可以通过命名空间获取变量
打印结果print(scope) print('-----------------分割线-----------------') print('a = ' + str(scope['a'])) print('f = ' + str(scope['f']))
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x037AAF40>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'testb.py', '__cached__': None, 'a': 10, 'b': 20, 'c': 'hello', 'd': True, 'e': 5.66, 'test_a': <function test_a at 0x038E8190>, 'test_b': <function test_b at 0x038E8148>, 'scope': {...}, 'f': ['a', 'b', 'c']} -----------------分割线----------------- a = 10 f = ['a', 'b', 'c']
递归
- 递归和循环类似,一般可以替换使用
- 递归要满足两个条件:
基线条件:递归不在执行的条件
递归条件: - 递归就是函数自己调用自己
# 这是一个递归的例子:求乘阶 def factorial(n): if n == 1: return 1 return n * factorial(n-1)
高阶函数
- 接收函数参数 或者 将函数作为返回值的函数
- 高阶函数 和 普通函数 的对比:
# 比如说我们想获取列表中所有偶数 def fn0(x): mlist = list() for s in x: if s % 2 == 0: mlist.append(s) return mlist # 可是我们需求又变了,想要奇数 def fn1(x): mlist = list() for s in x: if s % 2 == 1: mlist.append(s) return mlist
- 这样我们会多谢很多重复代码,所以我们可以使用高阶函数
一般情况下我们们的规则都是一次性的(只有一个地方只用),这样我们创建一个函数有些多余,此时我们可以使用匿名函数# 一个奇数原则函数 def fn3(n): return n % 2 == 1 # 一个偶数原则函数 def fn4(n): return n % 2 == 0 # 一个大于等于10原则函数 def fn5(n): return n >= 10 # 一个小于10原则函数 def fn6(n): return n < 10 # 根据不同原则函数 返回数据 def fn30(fun,lit): mlist = list() for n in lit: if fun(n): mlist.append(n) return mlist # 函数使用 print(fn30(fn3,array)) print(fn30(fn4,array)) print(fn30(fn5,array)) print(fn30(fn6,array)) # 这样我们就可以根据不同要求传入函数
匿名函数
- 语法:lambda 参数列表 : 返回值
- 多数作为参数使用
# 匿名表达式 示例 lambda a,b : a + b # 两个入参a和b,返回a,b的和
- 使用匿名函数完成高阶函数的使用
# 根据不同原则函数 返回数据 def fn30(fun,lit): mlist = list() for n in lit: if fun(n): mlist.append(n) return mlist # 使用 fn7 = lambda n : n%2 == 0 print(fn30(fn7,array)) # 或者 print(fn30(lambda n : n%2 == 0,array))
- 只能写一些简单的表达式
- map()
对每个元素操作
# 列表元素 加一 array = [2,1,5,6,4,51,8,45,17,84,61,14,32,16,49,48,7,9,3] r = map(lambda i : i + 1,array) print(list(r))
闭包
- 高阶函数中,函数返回值时函数,我们称之为闭包
- 闭包 一定是函数嵌套
- 写一个例子:
假如我们写一个数值累加器sums = 0 def sum(n): global sums sums += n return sums print(sum(1)) print(sum(2)) print(sum(3)) print(sum(4)) print(sum(5)) # 我们完成了一个累加器,但是这样的累加器很危险,如果我此时添加代码: sums = 1 print(sum(6)) # 此时的累加器数据一定不准确,因为在途中把变量改了,可能是无意的 ``` *使用闭包写一个数值累加器* ``` def make_sum(): sums = 0 def sum(n): nonlocal sums sums += n return sums return sum # 返回一个函数 sum = make_sum() print(sum(1)) print(sum(2)) print(sum(3)) print(sum(4)) print(sum(5)) # 使用闭包,我们在外面没办法直接调用sums,所以sums变量是安全的 ```