Python基础(5) 函数
函数是组织好的、可重复使用的、用户实现单一或者关联功能的代码段,能够提高应用模块性和代码的重复利用率。
1 函数定义和调用
(1)代码块以def关键字开头,后接函数标识符名称和圆括号()
(2)任何传入参数和自变量必须放在圆括号中间
(3)函数的第一行语句可以选择性的使用文档字符串(用于函数说明)
(4)函数内容以冒号起始,并且缩进
1.1 声明语法
Def 函数名([参数]): # 函数说明 要封装的代码块
1.2 调用语法
函数名([参数列表])
函数名其实是指向一个函数对象的引用,完全可以把函数名赋值给一个变量,相当于给这个函数起了一个别名。
def SayHi(): # 不带参 print(‘大家好!‘) SayHi() # 大家好! def AddNum(a, b): # 带参 return a+b print(AddNum(4, 2)) # 6
文档字符串:在函数体的第一个逻辑行的字符串,主要用于给函数示意,只能通过三个引号备注
def SayHi(): # 不带参 """这是不带参数的函数示例""" print(‘大家好!‘) print(SayHi.__doc__) # 这是不带参数的函数示例 SayHi() # 大家好!
2 函数的参数
2.1 必备参数
形参:定义函数时的参数;
实参:调用函数时的参数,形参和实参的位置必须一致,只有这样才能被正确匹配
2.2 关键字参数
传递给函数的名称-值对。直接在实参中将名称和值关联起来,因此向函数传递实参时不会混淆。
函数调用使用关键字参数来确定传入的值,并且允许参数顺序与声明时不一致,Python解释器能够用参数名匹配参数值。
def InFo(name, address): print(‘大家好,我叫%s来自%s!‘ % (name, address)) InFo(address=‘Hangzhou‘, name=‘Faith‘)
2.3 默认参数
也可以在定义形参的时候直接给参数添加默认值。带有默认值的形参必须放在右边,对应的实参可省略,以默认值代替,否则为实参值。
def SayHi(hi, times=2): print(hi * times) SayHi(‘Goodmorning! ‘, 1) # 实参值1 Goodmorning! SayHi(‘Goodmorning! ‘) # 默认值2 Goodmorning! Goodmorning! SayHi(‘Goodmorning! ‘, 3) # 实参值3 Goodmorning! Goodmorning! Goodmorning!
2.4 不定长参数
(1)*name:加星号 * 的参数会以元组(tuple)形式导入,存放所有未命名的变量参数,在传入额外的参数时可以不用指明参数名,直接传入参数值即可
def func(a, b, *args): print(a) print(b) print(args) func(1, 2, 3, 4, 5)# 1 # 2 # (3, 4, 5)
(2)**name:加两个星号 ** 的参数会以字典的形式导入,该方式返回的是字典,所以传入时需要指定参数名,否则会报错
def func(a, b, **args): print(a) print(b) print(args) func(1, 2, x=3, y=4, z=5)# 1 # 2 # {‘x‘: 3, ‘y‘: 4, ‘z‘: 5}
2.5 可变对象与不可变对象
不可变对象:字符串(strings)、元组(tuples)、标量(numbers),传递的是对象的值,不是对象本身,如果修改值,修改的是另一个复制的对象,不会影响原来对象
def fun(arg): args = ‘hello‘ print(args) str1 = ‘baby‘ fun(str1) # hello print(str1) # baby
可变对象:列表(list)、字典(dict)、集合(set), 传递对象自己,函数内部如果修改值会影响对象本身。
def fun(args): args[0] = ‘hello‘ print(args) list1 = [‘baby‘, ‘money‘] fun(list1) # [‘hello‘, ‘money‘] print(list1) # [‘hello‘, ‘money‘]
2.6 局部变量和全局变量
局部变量:只在函数体内有效
a = 1 def ChangeNum(a): # a为局部变量,不影响函数体外的值 a += 1 return a print(‘调用函数前,a的值为:‘, a) # 调用函数前,a的值为: 1 print(‘函数的返回值为:‘, ChangeNum(a)) # 函数的返回值为: 2 print(‘调用函数后,a的值为:‘, a) # 调用函数后,a的值为: 1
在函数内的变量前加“global”修饰,即可将变量更改为全局变量,对整个代码有效
def func(): global x print(‘函数体中x的值为:‘, x) # 函数体中x的值为: 50 x = 2 return x x = 50 print(‘调用函数前,x的值为:‘, x) # 调用函数前,x的值为: 50 print(‘函数返回的值为:‘, func()) # 函数返回的值为: 2 print(‘调用函数后,x的值为:‘, x) # 调用函数后,x的值为: 2
2.7 return语句
用于退出函数,选择性的向调用者返回一个表达式,直接return的语句返回None。注意:return后语句不再执行
def max(x, y): if x > y: return x else: return y num = max(1, 2) print(num)
return和print的区别:print是将结果输出到控制台,return是结束函数调用,并将结果返回给调用者,且返回的结果不能输出到控制台(不能直接打印),需要通过print才能显示出来
3 重要函数
3.1 lambda函数
匿名函数:没有具体名字
a = lambda x: x**2 print(a(4)) # 16
3.2 递归函数
递归就是子程序/函数直接调用自己或通过一系列调用语句间接调用自己,是一种描述问题和解决问题的基本方法。
def func(n): if n == 1: return 1 else: return n*func(n-1) print(func(5)) # 120
3.3内置函数
Python内置函数很多,可搜索资料练习。
3.4 高阶函数
一个函数可以作为参数传给另外一个函数,或者一个函数的返回值为另外一个函数(若返回值为该函数本身,则为递归),满足其一则为高阶函数。
函数式编程:
(1)函数本身可以赋值给变量,赋值后变量为函数;
(2)允许将函数本身作为参数传入另一个函数;
(3)允许返回一个函数。
#参数为函数 def bar(): print("in the bar..") def foo(func): func() print("in the foo..") foo(bar) # in the bar.. # in the foo.. # 返回值为函数 def bar(): print("in the bar..") def foo(func): print("in the foo..") return bar res=foo(bar) res() # in the foo.. # in the bar..
注:函数名(例如bar 、foo)为该函数的内存地址;函数名+括号(例如 bar()、foo() )-->调用该函数。
3.4.1 map( )
第一个参数是函数,剩下的参数是一个或多个序列,返回值是一个集合。
接收的是两个参数,一个函数,一个/多个序列,其功能是每次从序列中分别取出一个元素,带入函数处理,其返回值为一个迭代器对象。
print(list(map(lambda x: x**2, (1, 2, 3, 4, 5)))) # [1, 4, 9, 16, 25]
3.4.2 filter( )
第一个参数是函数,作用于多个元素。如果函数对象返回True,则该次的元素储存于返回的列表中。
filter( )通过读入的函数来筛选数据,其返回值也是一个迭代对象。
def func(a): if a > 100: return True else: return False print(list(filter(func, [10, 56, 101, 500]))) # [101, 500]
3.4.3 reduce( )
对于序列中的所有元素调用func( )进行数据合并操作,可以给定一个初始值。其返回值为一个值而不是迭代器对象,故其常用与叠加、叠乘等
from functools import reduce list1 = [2, 4, 6, 8, 10] nlist = reduce(lambda x, y: x+y, list1) print(nlist) # 30
4 生成器和迭代器
4.1 生成器
一边循环一边计算的机制,称为生成器:generator。生成器是一个特殊的程序,可以被用作控制循环的迭代行为,Python中生成器是迭代器的一种,使用yield返回值函数,每次调用yield会暂停,而可以使用next()函数和send()函数恢复生成器。
创建生成器:
方法1:把一个列表生成式的[]中括号改为()小括号,就创建一个generator
list = [x*x for x in range(10)] print(list) # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] generatro_ex = (x*x for x in range(10)) print(generatro_ex) # <generator object <genexpr> at 0x000002025CB27B88> for i in generatro_ex: # 生成器输出,使用next(generatro_ex)仅单次输出 print(i)
生成器打印:可以通过next()函数获得generator的下一个返回值;常规方法是使用for循环,因为generator也是可迭代对象。
方法二:将函数中的return替换为yield
yield把一个函数变成一个generator,带有yield的函数不再是普通函数,python解释器会将其视为一个生成器。在每次调用next()的时候执行,遇到yield语句返回,再次被next()调用时候从上次的返回yield语句处急需执行,也就是用多少,取多少,不占内存。
函数是顺序执行的,遇到return语句或者最后一行函数语句就返回。
def fib(max): n,a,b =0,0,1 while n < max: yield b a,b =b,a+b n = n+1 return ‘done‘ for i in fib(6): print(i)
4.2 迭代器(迭代就是循环)
迭代器包含有next()方法的实现,在正确的范围内返回期待的数据以及超出范围后能够抛出StopIteration的错误停止迭代。可以使用isinstance()判断一个对象是否是Iterator对象:
from collections import Iterator print(isinstance(‘abc‘, Iterator)) # False print(isinstance([], Iterator)) # False print(isinstance({}, Iterator)) # False print(isinstance((x for x in range(10)), Iterator)) # True
可以直接作用于for循环的数据类型有以下几种:
(1)集合数据类型,如list,tuple,dict,set,str等
(2)generator,包括生成器和带yield的generator function
这些可以直接作用于for循环的对象统称为可迭代对象:Iterable
可以使用isinstance()判断一个对象是否为可Iterable对象
from collections import Iterable print(isinstance(‘abc‘, Iterable)) # True print(isinstance([], Iterable)) # True print(isinstance({}, Iterable)) # True print(isinstance((x for x in range(10)), Iterable)) # True
由此可见list、dict、str虽然是Iterable(可迭代对象),却不是Iterator(迭代器)
这是因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。
Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。
4.3 小结
(1)凡是可作用于for循环的对象都是Iterable类型;
(2)凡是可作用于next()函数的对象都是Iterator类型,它们表示一个惰性计算的序列;
(3)集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。