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步操作:

  1. 将say传给info()函数 。
  2. 将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 小结

最后,我们将上面讲的内容通过一张思维导图来总结,具体如下

总结

最后我们将上面的介绍做个整体的回顾,整体涉及到了如下内容:

  1. 函数是什么 ?
  2. 函数的组成 。
  3. 函数如何调用?
  4. 函数可以有哪些返回类型 ?
  5. 函数中的值传递和引用传递
  6. 函数中常见的4种参数类型。
  7. 函数中的一些高级函数 。
(0)

相关推荐