python到底还有哪些函数我们还没见过?原来有这么多实用的函数
本文将将主要介绍如下内容
函数介绍
1.1 函数的定义
所谓的函数,其实就是具有特定功能的一段代码块,都可以独立的运行 。
函数有5部分组成,分别是定义函数的关键字,函数的名字,圆括号(存放函数的参数),函数功能的实现,函数的返回语句。
- 函数的关键字,定义函数要使用关键字def开头,后面跟着函数的名字 。两者中间相隔一个空格。
- 函数的名字,每个函数都要有一个名字即函数标识符,函数名字是由开发人员起的,起的名字最好有一定业务语义,比如此功能是一个加法的操作,那么就可以起add.
- 圆括号,圆括号内可以定义参数,参数可以是一个,也可以是多个,也可以没有参数 。圆括号后必须要有个冒号并且加缩进,
- 函数功能实现代码,冒号+缩进的后面就可以编写函数的功能代码块了,但这部分一般都是放在函数的下一行 。
- 函数的返回语句,函数使用return代表函数已经结束,不带表达式return代表返回None 。
函数的语法格式:
def 函数名(参数列表): 函数体 return
示例:
#不带参数的函数def hello_world(): print('hello world')
#带一个参数def show(msg) print(msg)
#带多个参数def add(x,y): z = x + y return z
1.2函数的调用
调用函数,其实就是使用函数。调用函数时直接使用函数名即可。
调用语法:
函数名(参数列表)
调用以上定义的函数
#1.调用hello_world函数hello_world()#2.调用show函数show('我是一个函数') #输出:我是一个函数#3.调用add函数print(add(3,4)) #输出:7
1.3函数的返回
函数的返回用于退出该函数,函数返回使用return进行表示,return后面可以根具体值,也可以不跟。如果不跟任何值代表返回None。
一般return后的值有以下几种情况:
- return None,如果是None,可以忽略不写 。
- return value,value可以是代码块中计算出来的值,也可以是一个表达式 。
- return val1,...,valn。return也可以返回多个值,注意:返回多个值时接收需用对应的多个变量。
示例
def show(msg): print(msg) return None #这样代码可以忽略。def add(x,y): z = x +y return z #返回某个值def add(x,y): return x + y #直接返回参与运算后的结果def calc(x,y): add = x+y sub = x-y return add,sub #返回了两个值
1.4 小结
最后,我们将上面讲的内容通过一张思维导图来总结,具体如下
函数的参数
在调用函数时,经常会用到形式参数(简称“形参”)和实际参数(简称“实参”),二者的主要区别是:
- 形式参数,是在括号内的参数就叫形式参数,例如上面add函数:
def add(x,y): #这里的x,y就是形式参数 z = x + y return z
- 实际参数,调用函数时,实际传入到函数内的值 。
print(add(3,4)) #调用add函数时,实际传入的3和4就是实际参数 。
需要注意的是,一个函数实际上可以接受任何类型的值,比如字符串,整数,浮点数,列表,元组,字典等。
当函数的形式参数中要传递实际的值时,也会分两种情况,不可变对象值(值传递)传递和可变对象值传递(引用传递)。
函数中的不可变对象值传递,示例:
x = 10def sum(a): a += 2 return aprint('调用函数后返回的值:',sum(x))print('x的值:',x)
运行后可以看出,无论函数中如何作用x的值,实际的x值是不会发生变化的 ,传入sum函数里的值其实就是x=10的一份副本 。
函数中的可变对象传递,示例
my_lst = ['java', 'c++', 'python','go']def update_lst(lst): lst.insert(1,'ruby') return lstprint('调用函数后返回的值:',update_lst(my_lst))print('my_lst的值:',my_lst)
运行结果:
以上结果可以看出,通过函数作用于my_list列表后,列表内的值也发生改变了,他们两同时指向了一个列表 。
最后,用个形象的例子总结一下,不可变对象就像是一个pdf文件,文件内容本身不能改变,但是可以拷贝,所以在函数中使用不可变对象都是使用的是拷贝后的副本。而可变对象就像是execl。execl中的内容是可变化的 。但是操作的都是源文件,函数中操作的可变对象也是对源文件里的内容进行操作 。
从参数的类型划分,可以将参数划分为:位置参数,默认参数,关键字参数,可变参数。
2.1 位置参数:
所谓的位置参数,主要是指输入函数内的参数数量和位置要和定义的形参中的一致。传多或传少都会报错。
#实例:def add(x,y): #定义了两个形参,调用该方法时必须传入两个参数值,传多或传少都会报错 z = x + y return z#输出print(1,2) #位置参数传入正确print('hello','world') #位置参数传入正确print(3) #不正确,报TypeError异常print(1,2,3) #不正确,报TypeError异常
2.2 默认参数
如果函数的某个参数经常会使用到一个值,那我们就可以给这个参数设置一个默认值 ,当调用该函数时,该参数若不传入值,就会使用到默认值。设置的默认参数需放在关键字参数后 。
示例:
#默认参数实例def show_lgg(msg,lgg='python'): info = ('{}{}'.format(msg,lgg)) return infoprint(show_lgg('使用默认的开发语言:'))print(show_lgg('使用默认的开发语言:','java'))#输出使用默认的开发语言:python使用默认的开发语言:java
从以上结果可以看出,函数show_lgg中的lgg设置了默认值python,当该参数没有传值时,使用的是默认的值。若该参数传入值后,使用的就是传入的值 。
那这个默认参数的用处就是,当函数的参数值频繁的使用某一个值或者优先被使用该值时,都可以将其设置为默认值 。比如我们经常使用的浏览器,那么多的浏览器中,我们最常用的Chrome,那么就可以将Chrome设置为默认值。不传入该参数时默认就会使用Chrome 。
使用默认参数时要注意,位置参数需放在默认参数前面,否则python会报错。使用它可以简化我们的调用,增加灵活度。
2.3 关键字参数
关键字参数是指传递给参数要以key=value形式,key代表函数中的形参名称,value就是传递给函数的实际的值。
使用关键字参数后,参数的位置就可以进行调换了 。
#关键字参数实例def student_lesson(grade,subject): z = '{}年级上{}课'.format(grade,subject) return zprint(student_lesson(3,'英语')) #位置参数print(student_lesson(grade=4,subject='数学')) #关键字参数print(student_lesson(subject='语文',grade=5)) #关键字参数可以调换位置print(student_lesson(6,subject='体育')) #关键字参数和位置参数混合使用。#输出3年级上英语课4年级上数学课5年级上语文课6年级上体育课
以上方式都可以正常正常调用,但是需要注意的是,当关键字参数和位置参数混合使用时,位置参数必须在前,关键字参数在后,否则会报错。
2.4 可变参数
这里的可变化参数指的参数的数量可以发生变化,比如我可以传递3个参数,也可以传递6个,甚至更多 。可变化参数有两种类型,分别是用来接收元组或列表形式的可变参数;另外一种是用来接收字典形式的可变参数 。
#列表形式的语法格式:def fun_name(a,b,*args): pass#代码实例def show_info(*args): print(args)show_info() #可变参数也可以不传入任何参数show_info('python') #传入一个参数show_info('python','java') #传入多个参数show_info(*['python','java']) #传入列表,注意,列表前需要加*show_info(*('python','java')) #传入元组,元组前也需要加*#输出:()('python',)('python', 'java')('python', 'java')('python', 'java')
在可变参数的参数名前需加*代,这样就代表该参数为可变参数了。同时也传入的时候可以是一个值,多个值,列表或元组 。
还有一种可变参数就是接收字典中的元素 。在函数中是以双星**表示。此函数同样能接收多个位置参数,但是传入的形式需要以关键字形式传递 。
#字典形式的可变参数语法格式def fun_name(a,b,**kw): pass #代码实例def show_info(**kwargs): print(kwargs)show_info() #可变参数也可以不传入任何参数show_info(key1='python') #传入一个参数show_info(key1='python',key2='java') #传入多个参数show_info(**{'key1':'python','key2':'java'}) #传入列表,注意,列表前需要加*#输出:{}{'key1': 'python'}{'key1': 'python', 'key2': 'java'}{'key1': 'python', 'key2': 'java'}
也可以将可变参数列表和字典形式结合起来使用 。
def fun_name(*args,**kwargs): #此函数可以接收任何长度,任何位置,任何关键字的参数 。 pass
针对以上四种参数,进行总体说明:
- 位置参数,参数的顺序和个数固定,调用函数时也必须按照该顺序和个数进行传递 。但如果参数个数固定了,就不容易扩展了 。所以,可以使用可变参数传入不同数量的参数 。
- 关键字参数,使用形参名称=输入的值来进行表示,如果使用关键字参数,就可以调换参数的位置 ,可变参数的字典形式其实就是关键字参数的扩展,可以支持不同长度的关键字参数。
- 默认参数,是针对位置参数的,相当于给某一个或几个位置参数设置默认值。
- 可变化参数,可以扩展位置参数的数量,支持列表形式和字典形式的位置参数 。
2.5 小结
最后,我们将上面讲的内容通过一张思维导图来总结,具体如下
高级函数
3.1 匿名函数
使用关键字lambda的表达式,又称为匿名函数。若函数可以用一行进行实现的话,那么该函数就可以使用匿名函数来代替。
#格式: lambda x,y,...,n:表达式#说明:x,y,...,n.相当于普通函数的位置函数,可以是一个,也可以是多个 。#实例:求两个数的和 #实现1:普通实现 def add(a,b): return a + b z1 = add(3,4) print(z1) #输出:7 #实现2:通过匿名函数 z2 = lambda x,y:x+y print(z2) #<function <lambda> at 0x0326C618> print(z2(4,5)) #输出:9
匿名函数除了可以赋值给变量外,还可以通过函数的参数来传递参数。
#实现:将函数的参数a,b传递给匿名函数def add(a,b): z = lambda :a+b return z
3.2 高阶函数-map,reduce,filter,sorted
map函数
map()是python的内置函数。map()函数接收两个或多个参数,第一个参数接收的是函数,后面的参数是序列。
#语法: map(函数,序列) #实例:将列表中的每一对元素相加lst = [(1,2),(3,4),(4,5)]total = map(sum,lst)print(list(total))#输出:[3, 7, 9]#说明: 以上代码相当于把(1,2),(3,4),(4,5)这三组中的每两个元素传递给sum,然后由sum来进行运算 。
map后面的序列也可以是多个
#语法: map(函数,序列1,序列2,...) #实例num = map(lambda x,y:x+y,[1,2,3,4,5],[6,7,8,9,10])print(list(num))#输出:[7, 9, 11, 13, 15]#说明: map的第二个,第三个参数都是列表,结果就是将这两个列表每一个位置的数进行相加
reduce函数
接收两个参数,第一个是函数,第二个是序列 。reduce()函数会把序列中的每一个元素累积进行计算,如何计算由前面的函数来定。
#语法: reduce(函数,序列) #实例:把列表中的数进行相加from functools import reducedef add(x,y): return x + ysum = reduce(add,[1,2,3,4,5])print(sum)#输出:15#说明:相当于是把整个列表的数进行了一次循环的相加操作
filter函数
也是接收两个参数,第一个是函数,第二个是序列,filter()函数会把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
#语法: filter(函数,序列) #实例:查询字典中成绩分大于350分的元素dt = {'grade_one': 237, 'grade_two': 532, 'grade_three': 356}dt1 = dict(filter(lambda x:x[1] > 350,dt.items()))print('查询年级分数大于350分的班级:{}'.format(dt1))#输出:查询年级分数大于350分的班级:{'grade_two': 532, 'grade_three': 356}
sorted函数
sorted函数对所有可迭代的对象进行排序
#语法 sorted(iterable, cmp=None, key=None, reverse=False) ==等价于下面的 sorted(可迭代对象,比较函数,用来比较的元素,排序规则) #实例: study = [('ruby', '3本', 150), ('python', '21本', 122), ('java', '18本', 210)] print('按第三个元素(价格)排序:{}'.format(sorted(study, key=lambda s: s[2]))) print('按第二个元素(书籍)降序排序:{}'.format(sorted(study, key=lambda s:s[2],reverse=True))) #输出: 按第三个元素(价格)排序:[('python', '21本', 122), ('ruby', '3本', 150), ('java', '18本', 210)] 按第二个元素(书籍)降序排序:[('java', '18本', 210), ('ruby', '3本', 150), ('python', '21本', 122)]
3.3 偏函数
偏函数其实就是functools包中的partial函数。
在前面我们讲到函数的默认值可以减少传递参数的数量,但是一旦定义了函数的默认值,如果修改的话,就得修改这个函数,很多情况我们底层的一些函数都是不建议修改的,这个时候可以使用偏函数来设置默认值,而且不同的类型可以设置不同的默认值 。
#简化版计算器def calc(x,y,op='+'): if op == '+': return x + y if op == '-': return x - y if op == '*': return x * y if op == '/': return x / y return x + y#需求:这时我们又需要进行大量的乘法操作,怎么办呢? 如下面的操作calc(2,3,'*')calc(2,4,'*')calc(2,5,'*')...calc(2,n,'*')#注意:这个时候不建议修改原函数,因为原函数也是综合考虑使用+最多的。不能这时用*就修改原函数为* 。#问题:每次传入的第三个值都是一个值,比较麻烦。能否把它作为默认参数传进来,#解决方案:使用偏函数from functools import partialmult=partial(calc,op='*') #把乘法固定print(mult(2,4)) #输出:8print(mult(6,4)) #输出:24#说明,使用偏函数后,参数op的默认值已经给定,所以后面不需要再传递该参数 。
3.4 递归
递归就是函数内部又调用了自己的函数就被称为递归 。
#语法格式def funA( ... funA() ...)#实例1:求:1!+2!+3!+4!+5!+...+n!def factorial(n): ''' n表示阶乘数的最大值 ''' if n==1: return n # 阶乘为1的时候,结果为1,返回结果并退出 n = n*factorial(n-1) # n! = n*(n-1)! return n res = factorial(5) print(res) #输出:120#===============================#实例2: 1,1,2,3,5,8,13,21,34,55,试判断数列第23个数是哪个?def fab(n): ''' n为斐波那契数列 ''' if n <= 2: ''' 数列前两个数都是1 ''' val = 1 return val val = fab(n-1)+fab(n-2) # 由数据的规律可知,第三个数的结果都是前两个数之和,所以进行递归叠加 return valprint(fab(23))#输出:28657
3.5 闭包和装饰器
闭包
闭包又称为闭合函数。简单来说,闭包的概念就是在函数内再定义一个函数,这个内部函数使用了外部函数的临时变量,且外部函数的返回值是内部函数的引用时,我们称之为闭包。
语法: def outer_func(): #外部函数 temp = null #外部函数的临时变量 def inner_func(): 函数体 return inner_func #将内部函数作为返回值返回 #实例import operatordef operating(op): #外部函数定义具体的操作符 def calc(a,b): #内部函数定义计算的两个变量 oprt = { '+': operator.add(a,b), '-': operator.sub(a,b), '*': operator.mul(a,b), '/': operator.truediv(a,b) } return oprt.get(op) return calc #把内函数的引用当做外函数的变量值返回add = operating('+')print(add(3,4)) #输出:7
那么,闭包最主要的用途就是装饰器 。
装饰器
装饰器的本质其实就是一个闭包函数,以下就是一个最为普通的装饰器,
def info(func): def wrapper(): print('[INFO]: 调用 {}()'.format(func.__name__)) return func() return wrapperdef say(): print('hello!')say_hello = info(say) # 添加功能并保持原函数名不变
通过和闭包进行相比较,两者的写法都是一样的,其实装饰器就是一个闭包函数 。那么除了以上调用方法外,python在后续的版本又引入了@语法糖,同样是上面的代码,可以使用如下方式调用 。
def info(func): def wrapper(): print('[INFO]: 调用 {}()'.format(func.__name__)) return func() return wrapper@infodef say(): print('hello!') say()#输出:[INFO]: 调用 say()hello!
这样的话,就通过@ 标识符将装饰器应用到函数上了 。其实通过以上的调用,实际上是执行了如下2步操作:
- 将say传给info()函数 。
- 将info函数执行完成的返回值反馈say
以上是一个最简单的装饰器,但有一个问题,如果被装饰的函数需要传入参数,那么这个装饰器就不能用了 ,因为返回的函数并不能接受参数 。
带参数的函数装饰器
通过制定装饰器函数wrapper接受和原函数一样的参数,就可以参数了 。
def info(func): def wrapper(arc): # print('[INFO]: 调用 {}()'.format(func.__name__)) return func(arc) return wrapper # 返回包装过函数@infodef say(arc): print('hello {}.'.format(arc)) say('装饰器函数') #输出[INFO]: 调用 say()hello 装饰器函数.
以上还是只能接受一个参数,但实际情况下,同一个装饰器可用于多个不同的函数,每个函数所带的参数都各不相同,怎么解决呢 ?
这时我们就可以用到可变参数*args和**kwargs了。通过这两个参数,装饰器就可以用于任意函数了 。
def info(func): def wrapper(*args, **kwargs): # 指定*args和**kwargs,接收任意参数 print('[INFO]: 调用 {}()'.format(func.__name__)) return func(*args, **kwargs) return wrapper # 返回@infodef say(arc1): print('hello {}!'.format(arc1))@infodef todo(x,y): print(x * y)say('作用于多个函数的装饰器')todo(3,5)#输出:[INFO]: 调用 say()hello 作用于多个函数的装饰器![INFO]: 调用 todo()15
3.6 回调函数
简单的来说,把一个函数作为参数传给另一个函数,那么这个函数就是回调函数。这个被传入的参数其实是函数指针,即指向一个函数的指针(地址)。
def calc(func,a,b): #第一个参数接收一个函数 return func(a,b)def add(x,y): return x + ydef mul(x,y): return x * yprint(calc(add,3,5)) #第一个参数为函数,后面的两个参数是函数的参数print(calc(mul,3,5)) #输出815
回调函数还可以进行异步调用,即非阻塞调用,通常用在多线程或者多进程中。
3.7 生成器函数
生成器函数,就是说定义函数时,内部带yield就算生成器函数。在调用生成器运行的过程中,每次遇到 yield 就会返回 其后的值, 并在下一次执行 next() 方法是从当前位置继续运行。
def scq_func(): for x in range(1,10): yield x * 2s = scq_func()print(s) #输出:<generator object scq_func at 0x0161B510>print(next(s))print(next(s))print(next(s))print(next(s))#输出:<generator object scq_func at 0x0161B510>2468
以上函数在执行过程中,遇到yield就中断下次又继续执行,直到没有yield可执行了,再调用next就会报错 。
那该函数主要的作用是什么呢 ? 在数学中有很多的计算都是无限穷尽的(比如自然数) 。我们不可能通过序列去存放,这时我们就可以使用生成器按照规则计算下出一个数据,可以计算出很大的数 。
import sys def fibonacci(n): # 生成器函数 - 斐波那契 a, b, counter = 0, 1, 0 while True: if (counter > n): return yield a a, b = b, a + b counter += 1f = fibonacci(10) # f 是一个迭代器,由生成器返回生成 while True: try: print (next(f), end=' ') except StopIteration: sys.exit() #输出:0 1 1 2 3 5 8 13 21 34 55
3.8 小结
最后,我们将上面讲的内容通过一张思维导图来总结,具体如下
总结
最后我们将上面的介绍做个整体的回顾,整体涉及到了如下内容:
- 函数是什么 ?
- 函数的组成 。
- 函数如何调用?
- 函数可以有哪些返回类型 ?
- 函数中的值传递和引用传递
- 函数中常见的4种参数类型。
- 函数中的一些高级函数 。