python 基础 3- 函数

本贴最后更新于 270 天前,其中的信息可能已经事过境迁

函数

函数可以认为是提供一种功能,当我们需要使用该功能时,就可以来调用该函数。

Python 中,函数可以分为两种:

内建函数:可以直接去访问的函数,例如 print,input,id,len 等

自定义函数:当我们需要的功能,Python 没有提供给我们时,我们可以自行来实现。

为什么使用函数:我们要实现的功能,未必只使用一次,有可能使用多次,甚至可能多人都需要使用。如果我们在每次使用该功能时,都给出对应的代码实现,就会造成代码重复。一旦代码重复,以后需求变更时,所有重复的代码都需要进行修改。此时程序非常难以维护。

需求:有效避免代码的重复? 答案:通过自定义函数实现

函数的优势:

  • 函数实现了代码的重用,避免了代码的重复。以后功能变更时,只需要修改函数定义处即可,函数调用处不需要进行任何的改变。
  • 函数有利于对程序进行模块的划分,便于协作式开发。

函数的定义

def 函数名(参数列表):

`函数体(即函数功能的实现)`​

定义函数后,函数体是不会执行的,必须调用函数才能得到执行:函数名(参数列表)

当调用函数后,程序列成就会从调用函数的位置,跳转到函数定义位置,执行函数体。调用完毕后,程序流程返回函数调用处,继续执行后续的语句。

step in :进入函数调试

step out:从函数中出来

函数的参数

函数是具有一定功能大代码片段,函数的参数是对功能细节的调整做出的设置

在函数定义时,函数参数称为形式参数(行参),在函数调用时,提供的参数称为实际参数(实参),在函数调用过程中,实际参数会赋值给对应的形式参数。

#定义一个可以控制输出星星的行数和个数,并且可以将星星换成别的符号的函数
def print_start(lines,char,num_per_line):
    for i in range(lines):
        print(char*num_per_line)
#lines:输出的行数/  char:输出的字符/ num_per_line:每一行的字符数
#在调用函数时需要传递对应的参数值
print_start(2,'*',30)

尝试:通过函数交换变量的值——无法实现,因为

函数在调用时,实际参数仅仅是赋值给形式参数,而并非二者成为一体,因此,在函数内部,对形式参数做出的改变是不会反作用于实际参数的,但是可以改变实际参数所关联的内容。

def swap1(a,b):
    c=a
    a=b
    b=c

但是可以通过列表来交换变量,因为 list 改变的是对象的内容,而不是改变实际参数所绑定哪个对象

def swap2(li):
    li[0],li[1] = li[1],li[0]

函数的返回值

return

当函数具有返回值,相当于是函数的返回值,替换了调用的函数。

如果函数中没有显示使用 return 语句来返回结果,此时相当于是执行 return None。

函数中如果遇到了 return 语句,则会停止函数的执行,将 return 的结果返回给调用端。return 之后的语句将不会得到执行。

def 函数名(参数列表):

`函数体`​

return 表达式

def add(a,b):
    sum_=a+b
    return sum_
#可以理解为将sum_赋值给result
result=add(5,3)

函数返回多个值

一个函数只能执行一条 return 语句,可以将多个值放入一个容器中,将容器返回。

def comput(a,b):

return a+b,a-b,a*b,a/b​ 返回的是元祖

指导思想:通常情况下,如果函数需要返回多个值,我们会将多个值放入一个元祖中,然后返回。因为元祖是不可修改的,可以防止调用端以外修改我们的数据

参数的默认值

参数多,函数调用时,也会比较繁琐,因此在定义函数的时候要为参数设置默认值。当参数具有默认值时可以选择性的传递。当我们为形参指定实参时,实参会赋值给形参,否则形参取定义时的默认值。

注意:可选参数必须定义在必选参数之后

参数可以具有默认值,也可以没有默认值。如果函数中有的参数有默认值,有的没有,则具有默认值的参数一定要定义在没有默认值的参数之后。

def 函数名(参数1=默认值1,参数2=默认值2,……)

`函数体`​
def print_start(lines=2,char='*',num_per_line=20):
    for i in range(lines):
        print(char*num_per_line)
print()#参数具有默认值,因此可以不指定实际参数

有时候,函数的参数可能会很多,如果这些参数都需要用户进行设置,会带来很多的不便性。因此,我们可以为参数指定默认值。一旦参数具有默认值,该参数就是一个可选项,用户就可以选择性传递。

位置参数和关键字参数

如果用户只想设置第 3 个参数 num_per_line,将其设置为 30,而前两个参数取默认值,该如何做。

在 python 中,实际参数可以使用两种方式来指定

  1. 位置参数:对位传递,对位执行实参赋值给形参,print_star(2,'*',30)

  2. 关键字参数:函数传递是以 参数名=参数值 的形式指定

    print_star(lines=2,char='#',num_per_line=20)

关键字参数的优势

  1. 关键字参数比位置参数具有更好的可读性。
  2. 关键字参数可以不同考虑参数的顺序,只要参数名正确即可
  3. 关键字参数在多个参数具有默认值时,可以实现更灵活的传递与选择。

位置参数与关键字参数可以混合使用,如果混合使用,位置参数必须放在关键字参数之前。同时不要将同一个参数多次传值。

print_star(2,'#',num_per_line=20)

print_star(line=2,'#',30)​错误,关键字参数必须在位置参数之后。

print_star(2,line=2)​错误,line 这个参数穿了两次,同一个参数不能传多次

可变参数(不定长参数)

可变参数可以用来接收任意数量的位置参数或者关键字参数。

可变位置参数:参数名,底层是使用一个元祖来实现的,一般起名* args

可变关键字参数:参数名,底层是使用一个字典来实现的,*一般起名 kw*args

def add(*params):
    sum_=0
    for p in params:#元祖遍历
        sum_+=p
    return sum_
add()
add(1)
print(add(1,2,3,4,5,6))#多个参数
def fun(**kwargs):
    #print(type(kwargs))
    for k,v in kwargs.items():#字典遍历
        print(k,v)
fun(a=1)
fun(a=1,b=2,c=3)#多个参数

可变关键字参数可以接受未匹配的关键字参数,如果关键字参数已经匹配,则无法传入可变关键字参数中。

def fun(a,**kwargs):
    #print(type(kwargs))
    for k,v in kwargs.items():
        print(k,v)
fun(a=1,b=2,c=3) #执行结果只有b 2 和c 3,因为a=1已经赋值给a=1了

参数传递时的拆包操作

在传多个参数的时候,这些参数未必都是以单个变量的形式存在

,可能多个参数值存放在容器(列表,元祖,字典)中,此时需要将容器拆包,可以实现更方便的参数传递。

列表——*
def fun(a,b,c):
    pass
li=['王同学','张同学','马同学']
fun(*li)
#星号相当于执行 fun(li[0],li[1],li[2])
字典——**
d={'a':'张三','b':'李四','c':'王五'}
fun(**d)
#两个星号相当于执行
fun(a='张',b='李',c='王')

例题:想要输出 t 中的元素,元素之间用#分隔,最后用&结尾

image

递归

递归是函数调用自身的过程

函数不会无条件调用自身。否则会达到最大深度,产生错误(RecursionError)。

递归的思想:递归采用分而治之的思想,着重于问题的拆解。我们通过递归,将大问题,拆解成小问题,小问题拆解成更小的问题,直到问题不需要再进行拆解,直接得出答案,我们将该答案进行回溯,一直回溯到最初的问题,问题得以解决。

递归调用的只是跟自己长的一样的函数,对自己本身并没有影响。

递归可以分为两个过程:

  1. 递推:将问题逐渐拆解,拆解成最小的问题,能够直接得到答案。
  2. 回溯:利用递推的答案,得到更大的问题的答案,直到解决最终的任务。

递归的通用写法:

def f(问题):

if 是否可以直接得到答案?(是否不进行拆解)

	return 答案

问题拆解

f(小问题)

关于循环与递归

  • 二者具有一定的相似之处,通常情况下,也能够解决同一个问题。但是,二者的思想是不同的。对于循环来说,考虑的是重复性操作。我们将同一个任务执行多次。
  • 对于递归来说,考虑的是问题的拆解,将大问题拆解成更小的子问题。

循环与递归的对比

通常情况下,二者能够完成同一个任务

循环在执行时,不需要创建额外的数据结构,但是,递归涉及到函数的调用(对自身的调用),因此,在执行过程中,需要创建额外的栈。

但是有些任务,通过问题拆解来实现会比较简单,通过循环这种歌重复性的思想操作实现,会相对困难(例如汉诺塔),因此,此时应该优先使用递归实现。

例题:3 的阶乘,斐波那契数列、汉诺塔程序、输入一个字符串(字符不重复)输出该字符串的所有全排列

def factorial1(n):
    if n==1:
        return 1
    else:
        return factorial1(n-1) * n

1711507280276

  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    545 引用 • 672 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...