·函数(function)
笔记:
在数学中,一次函数y = 8x + 2,也可以写成:f(x) = 8x + 2
当x = 1时,表达式f(1) = 8*1+2 = 10;
当x = 2 时,表达式f(2) = 8*2+2= 18。
函数 f(x) 花括号中的 x 用来接收一个参数,8x + 2 可以计算传入参数相对应的值。
在Python中,可以把函数抽象成数学中的一次函数,在程序中多次执行同一项任务时,并不需要反复编写相同的代码。可以通过编写函数,在需要时可以多次调用该函数实现相同的功能。函数能提高应用的模块性,和代码的重复利用率。Python提供了许多内建函数,比如print()。当然,也可以自建函数,这被称作用户自定义函数。
@ 函数的定义
函数定义的规则:
- 函数以'关键字def'+'函数名'+'圆括号(参数)'+'冒号:'。
- 圆括号中的参数可以用于定义参数。
- 函数内的第一行语句可以选择性地给函数编写文档用于说明函数的作用。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return默认返回 None
def 函数名(参数): #声明一个函数 ... 函数体 ... 返回值函数名(参数) #调用函数
@ 向函数传递参数
创建函数:
def test_1(name,age,add): #形参共接收3个参数 '自我介绍' #编写函数文档 (可以用 test_1.__doc__ 打印函数文档) print("My name is {},I'm {} years old.".format(name,age)) print("I'm from {}.".format(add))
调用函数:
#调用函数,并传入实参 test_1('Jimmy',18,'shanghai') #实际上就是给参数赋值 打印结果: My name is Jimmy,I'm 18 years old. I'm from shanghai.
位置实参传递参数:
#再次调用函数,并传入实参 test_1('shanghai','Jimmy',18) 打印结果: My name is shanghai,I'm Jimmy years old. I'm from 18.
当使用位置实参来调用函数时,实参顺序不正确,将导致结果出乎意料。
关键字实参传递参数:
#调用函数,并传入关键字实参 test_1(add='shanghai',name='Jimmy',age=18) 打印结果: My name is Jimmy,I'm 18 years old. I'm from shanghai.
通过关键字实参向函数传递参数,无需考虑函数调用中的实参顺序。
默认值参数:
def test_1(name,age,add='beijing'): #add参数默认值设置为‘beijing’ '自我介绍' print("My name is {},I'm {} years old.".format(name,age)) print("I'm from {}.".format(add)) #调用函数,并传入实参 test_1('Jimmy',18,'shanghai')打印结果:My name is Jimmy,I'm 18 years old.I'm from shanghai.
这次传入两个参数,看看结果会怎样:
test_1('Jimmy',18) 打印结果: My name is Jimmy,I'm 18 years old. I'm from beijing.
结果表明:调用函数时没有给add传入参数,Python将把值默认设为'beijing'
注意:在使用默认值时,形参列表中必须先列出没有默认值的形参,再列出有默认值的形参,这样才能正确解读位置参数。
@ 函数的返回值:
带有返回值return的函数:
def test_return(): print("My name is python.") return #这里的retrun可以理解为循环的break,它的目的是为了跳出函数 print("hello")
调用函数:
x = test_return() 打印结果: My name is python.
当执行到return语句时,结束函数,跳过了print("hello")语句。
有个疑问,return 没有返回任何值,那么x指向什么呢?
试着打印x的结果:
x = test_return() print(x)打印结果:None
由此可得,每个函数都有返回值,如果 return 没有指定返回什么值,那就返回一个None。
@ 收集参数:
向函数传递列表:
def test_2(names): for name in names: #这里使用for循环遍历x ,如果没有for循环,打印结果将是整个元组 print("hello,%s."%name) x = ('python','java','c++') test_2(x) 打印结果: hello,python. hello,java. hello,c++.
收集任意数量的参数:
def test_2(*names): #在接收任意数量的形参可以在names前加*号 print(names) test_2('python','java','c++') 打印结果: ('python', 'java', 'c++')
传入*names的值将放在一个元组里
def test_2(*names): print(names) test_2() 打印结果: ()
如果没有可供收集的参数,*names将是个空元组。
位置参数和任意数量参数:
def test_2(name_1,*names): print(name_1,names) test_2('python','java','c++') 打印结果: python ('java', 'c++')
位置参数name_1为'python',余下的参数将被*names收集放入一个元组中。
def test_2(name_1,*names,name_2): #*names不收集关键字实参 print(name_1,names,name_2) test_2('python','java','c++',name_2="c")打印结果:python ('java','c++') c
带星号的参数可以放在其他位置(不放在最后),但在这种情况下,要使用关键字赋值,增加额外的工作,所以建议*names放最后。
收集任意数量字典:
def test_2(**names): #在接收任意数量字典可以在names前加**号 print(names) test_2(A='python',B='java',C='c++') 打印结果: {'A': 'python', 'B': 'java', 'C': 'c++'}
同时收集多个参数、多个字典:
def test_2(name_1,**names,*name_2): print(name_1,names,name_2) test_2('function',A='python',B='java',C='c++') File "hello.py", line 1 def test_2(name_1,**names,*name_2): ^SyntaxError: invalid syntax
为什么语法错误呢?接着往下看:
def test_2(name_1,*name_2,**names): #调整*names和**names位置,程序正常运行 print(name_1,names,name_2) test_2('function',A='python',B='java',C='c++')打印结果:function {'A': 'python', 'B': 'java', 'C': 'c++'} ()
收集任意数量的形参应在前,收集任意数量字典的形参在后。
@ 参数分配:
分配元组:
def test_2(name,name_1,name_2): print(name,name_1,name_2) x =(2,3,5) test_2(*x)打印结果:2 3 5
分配字典:
def test_2(name): print(name['A'],name['B'],name['C']) x = {'A':'python','B':'java','C':'c++'} test_2(**x)Traceback (most recent call last): File "不.py", line 5, in <module> test_2(**x)TypeError: test_2() got an unexpected keyword argument 'A'
正确的请看下面:
def test_2(name): print(name['A'],name['B'],name['C']) x = {'A':'python','B':'java','C':'c++'} test_2(x)打印结果:python java c++
def test_2(**name): print(name['A'],name['B'],name['C']) x = {'A':'python','B':'java','C':'c++'} test_2(**x)打印结果:python java c++
在定义和调用的时候同时使用*或**,将只传递整个元组或者整个字典,没有实际意义。
使用*或**收集参数时,只有在定义函数(收集任意数量参数时)使用;而分配参数时,只有在调用函数(拆分字典或者序列时)时使用。
@ 全局变量与局部变量:
定义全局变量x = 2;局部变量x = 10
x = 2 #全局变量 def test_3(): x = 10 #局部变量 print(x) test_3() print("------------") print(x)打印结果:10------------2
根据打印结果,第一个x的值是函数内部x的值为10,而第二个的值是函数外部x的值为2,两个变量名虽然都一样,但是彼此的值没有相互影响。
因此,在函数内部的变量称作局部变量,而函数外部的变量称作全局变量。
使用globals()访问全局变量:
x = 100 def test_3(): globals()['x'] #局部变量使用全局变量 print(x) test_3() print("------------") print(x)打印结果:100------------100
声明x是全局变量:
x = 100 def test_3(): global x #声明全局变量 print(x) test_3() print("------------") print(x)打印结果:100------------100
因此,在函数中想要使用全局变量,可使用Python保留字global。
x = 2 def test_3(): global x x += 1 print(x) test_3() print("------------") print(x) 打印结果: 3 ------------ 3
函数内,可以用global访问全局变量,并有权限修改全局变量的值。
使用locals()函数:
def test_3(a): x = 1 print(locals()) test_3(2) 打印结果:{'a': 2, 'x': 1}
locals()函数返回一个包含局部变量的字典。
@ 命名空间Namespace:
作用域:表示变量(标识符)的可见范围。一个标识符在多个作用域中被定义,它们的作用不会有任何的冲突。
例如:Bill是X公司的员工,工号为123,而John是Y公司的员工,工号也是123。由于两人在不同的公司工作,可以使用相同的工号来标识而不会造成混乱,这里每个公司就表示一个独立的作用域。如果两人在同一家公司工作,其工号就不能相同了。
Python中唯一的映射是字典,里面有键值对,变量和值的关系就有点像映射。来看一个例子:
x = 100 scope = vars() print(scope['x']) 打印结果: 100
正如前面所提到的全局变量和局部变量,即使它们的变量名一样,但是它们在不同个命名空间,所以对应的值不会相互影响。
@ 函数的递归:
函数递归(迭代)可以理解为函数中自己调用自己,下面来看经典递归函数:
阶乘:
def fei(n): #阶乘的个数 if n == 1: #if条件语句:当n等于1时返回值1 return 1 else: #条件不成立时,运行else语句 return n * fei(n-1) print(fei(3))打印结果:6
笔记:函数传入参数值3 >>> if条件判断不成立,运行else语句:n * fei(n-1) >>> 3 * fei(2),此时n=2 >>> 函数传入参数值2 >>> if条件判断不成立,运行else语句:n * fei(n-1) >>> 3 * 2 * fei(1) ,此时n = 1 >>> if语句成立,函数返回1 >>> 3*2*1打印结果为6
斐波那契数列:
def fibs(num): if num == 0: return 0 if num == 1: return 1 else: return fibs(num-1) + fibs(num-2) print(fibs(10)) 打印结果: 55