10 函数:

初识函数

函数的作用

正如老师在课堂反复强调过,编写代码要不断追求简洁和易读。换句话说,我们要尽量避免写重复的代码,少复制粘贴,也就是所谓的DRY原则——Don't Repeat Yourself。

前面学习过的循环是减少代码重复的一种手段,那么接下来要学习的函数则是Python里增强代码重用性最有力的工具,它的定义是:

10 函数:

什么意思呢?我们之前所写的代码都是立即运行且只能被执行一次,而函数可以让我们整合打包好代码,以便这些代码能够随时被复用,这样能极大地减少代码冗余。往后,随着我们想要实现的功能越来越复杂,代码可能会有几百上千行,这样对写代码和读代码都是一个挑战。如果将一个程序用函数拆分成多个独立的子任务来完成,就会让代码结构变得清晰许多。

函数的组成

我想你第一次听到这个词,大概率是在初中的数学课堂上,那就先让我们回忆下吧。

y = 3x + 5是个标准的一次函数,x是自变量,3x + 5是执行过程,自变量x决定了y的输出值。

函数(Function)能实现的功能从简单到复杂,各式各样,但其本质是相通的,我们可以看作成三个部分。

10 函数:

在Python中,函数也是如此,“喂”给函数一些数据,它就能内部消化,执行相应的功能,最终给你“吐”出你想要的东西,就好比自动贩卖机一样。

10 函数:

以上“陈列的”就是我们之前接触过的Python函数,它们是Python本身就自带的函数,所以也叫内置函数,提供了许多我们常用的基本功能。你还会发现图片里的函数后面都跟了个括号。括号里放的东西,也就是我们需要输入给函数的数据,它在函数中被称作【参数】。【参数】指向的是函数要接收、处理怎样的数据(你也可以把它理解成自变量)。

比如len()函数会根据括号里放的参数的不同,输出(返回)不同的值。

10 函数:

括号里面的字符串和列表,都是len() 函数的参数。

10 函数:

定义和调用函数

就像贩卖机不总是有我们想要的东西。除了Python自带的内置函数,我们能不能根据自己的需要,自己定义一个独一无二的函数呢?答案是肯定的,下面我就来教大家如何DIY一个函数,这也是今天的重点。

10 函数:

照着这个语法,我们以刚刚提到的y = 3x + 5 为例子,来定义一个一次函数。

10 函数:

来一起读代码。第1行:def的意思是定义(define),math是【函数名】(自己取的),再搭配一个英文括号和冒号,括号里面的x是参数(参数名也是自己取)。
第2行:def下一行开始缩进的代码就是函数要实现的功能,也叫【函数体】。这里的功能就是:根据x计算出一个值y。
第3行:return语句是返回的意思,可以指定函数执行完毕后最终会返回什么值或表达式,否则计算机是无法判断出函数最终要输出什么结果的。

10 函数:

练习:定义一个新的函数,表达式是y = x² + x

10 函数:

我们只是完成了【定义函数】,定义函数只是将函数的内部功能封装起来(组织好),它们就像是神奇宝贝里的精灵球,安静地待着,只有听见你的召唤时才会出场,为你所用。那么该怎么调用函数呢,让它发挥作用呢?很简单,就是输入函数名和参数所对应的值,这个过程在函数里叫作参数的传递(pass)。
比如说刚刚的例子,我们希望计算出当x = 10时y的结果

10 函数:

math(10)的意思是将整数10赋值给参数x并运行该函数。函数执行完毕后最终返回了y的值即110,然后将这个结果赋值给变量a,再用print()将变量a打印出来。当然,你也可以只用一行代码print(math(10))来表示同样的意思。

练习:

我们之前接触过的len()函数是Python的内置函数,功能之一是可以返回字符串的长度。那么,我们可不可以自己写一个具有相同功能的函数呢?

答案是肯定的,老师在这里提供一个实现思路,1. 设置一个初始为0的计数器;2.遍历字符串,每遍历一次,计数器加一;3.返回计数器的值。

现在,请你写出一个能计算字符串长度的函数,然后传递参数'三根皮带,四斤大豆'来调用函数,并将结果打印出来。

10 函数:

函数的进阶使用

Python支持非常灵活的参数形态,从0个参数到多个参数都可以实现

我会主要介绍函数中常见的位置参数、默认参数和不定长参数。

接下来,我会用一个场景将例子串起来,这个场景就在本关标题里 —— 深夜食堂营业记!深夜食堂,开张!

位置参数

10 函数:

我们可以看到,这里定义了一个opening()函数,但是括号里没有带参数,原因是这个函数的功能是打印出固定的三句话,不需要参数的参与。需要强调的是,即便是没有参数,我们在调用函数的时候也不能省略括号,如此例中的opening()。在这个例子里,也不需要return语句,原因是这个函数直接在屏幕打印出内容,不需要返回值。事实上,没有return语句的函数,Python也会在末尾隐性地加上return None,即返回None值。

换句话说,有时候我们需要的只是函数的执行过程(比如打印内容),有时我们需要函数执行完毕的返回结果。

那接下来,我们来看看有多个参数的情况。

在开业初期,为了吸引人流,你采取的策略是顾客可以任意点菜。但因为人手不足,所以只能为每个人提供一份开胃菜和一份主食。如果写成函数的形式,这个函数就会有两个参数。

10 函数:

这里的'话梅花生'和'牛肉拉面'是对应参数的位置顺序传递的,所以appetizer和course被叫作【位置参数】,当有多个参数的时候,我们就必须按照顺序和数量传递,这也是最常见的参数类型。

如果不按位置顺序传递,就会闹出乌龙。

10 函数:

默认参数

回到这个食堂,经营一阵之后,为了吸引更多的人流,你决定给每个顾客免费送上一份甜品绿豆沙,这时候你就可以用到【默认参数】,直接在定义函数的时候给参数赋值。

需要注意的是:默认参数必须放在位置参数之后。我们来看看代码:

10 函数:

如果一个函数的某个参数值总是固定的,那么设置默认参数就免去了每次都要传递的麻烦。不过默认参数并不意味着不能改变,试试运行下列结果。

10 函数:

一个萝卜一个坑,因为前两个位置参数已经有对应的值传递,Python会自动将'银耳羹'传递给参数dessert,替换了默认参数的默认值。

不定长参数

后来呢,盛夏来袭,你觉得卖烧烤是个不错的主意。但问题是每个人点的烤串数量都不同,你也不能限定死数量,这时候【不定长参数】就能派上用场,即传递给参数的数量是可选的、不确定的。

它的格式比较特殊,是一个星号*加上参数名,它的返回值也比较特殊,我们来看看下面的例子。

10 函数:

你会发现函数返回的是这样的结果:('烤鸡翅', '烤茄子', '烤玉米'),我们用type()函数可以知道这种数据类型叫作元组(tuple)

元组的写法是将数据放在小括号()中,它的用法和列表用法类似,主要区别在于列表中的元素可以随时修改,但元组中的元素不可更改。

和列表一样,元组是可迭代对象,这意味着我们可以用for循环来遍历它,这时候的代码就可以写成:

10 函数:

可能目前我们不会怎么用到不定长参数,不过了解这一个概念可以帮助我们看懂一些函数文档。

比如说我们最熟悉的print()函数,它完整的参数其实是长这样的:

10 函数:

可以看到第一个参数objects带了*号,为不定长参数——这也是为什么print()函数可以传递任意数量的参数。其余四个为默认参数,我们可以通过修改默认值来改变参数,对比一下下列代码的输出结果。

10 函数:

返回多个值

函数,不仅可以支持输入多个参数,而且也可以同时输出多个值吗。接下来,我们就来讲讲如何用return语句来返回多个值。

依旧回到我们的食堂,后来你决定推出不定额的优惠券,到店顾客均可参与抽奖:5元以下随机赠送一碟小菜,5-10元随机赠送一碟餐前小菜和一个溏心蛋。那么代码就可以写成这样(注:随机功能我们可以用random模块中的random.choice()函数来):

10 函数:

可以看到:要返回多个值,只需将返回的值写在return语句后面,用英文逗号隔开即可。

10 函数:

切记!我们不能写成上面这种两行的格式,是因为函数在执行过程中遇到第一个return语句就会停止执行,所以第二个return '溏心蛋'永远不会被执行。

接下来我们直接运行下列的代码,看看返回的结果是什么数据类型。

10 函数:

没错,表示返回的数据类型又是我们刚才提到的元组。在这个例子中,该元组是由两个元素构成的。

元组和列表一样,可以通过索引来提取当中的某个元素

10 函数:

另外一种方式:我们也可以同时定义多个变量,来接收元组中的多个元素

10 函数:

多个函数间的配合

变量作用域

在实际编程中,我们会用函数来封装独立的功能,所以一个程序往往是通过多个函数的配合来实现的。当多个函数同时运行时,就涉及函数中一个非常重要的概念 —— 变量作用域。

什么是变量作用域呢?我们还是先来看看一个例子。

月底了,身为老板的你要核算成本来调整经营策略,假设餐馆的成本是由固定成本(租金)和变动成本(水电费 + 食材成本)构成的。那么我们可以分别编写一个计算变动成本的函数和一个计算总成本的函数,请阅读下列代码:

10 函数:

乍一看代码好像没有什么问题,但是一旦运行,终端就会报错。请你先阅读下报错信息,我马上会来解释。

10 函数:

可以发现第一个函数cost()运行没有问题,报错信息指出问题出在第10行,sum_cost()函数内的变量variable_cost没有被定义。

这就涉及一个变量作用域的问题:程序中的变量并不是在哪个位置都可以被使用的,使用权限决定于这个变量是在哪里赋值的。关于这个概念,目前我们只需要掌握下面两点即可:

10 函数:

第一点:一个在函数内部赋值的变量仅能在该函数内部使用(局部作用域),它们被称作【局部变量】,如cost()函数里的variable_cost。

第二点:在所有函数之外赋值的变量,可以在程序的任何位置使用(全局作用域),它们被称作【全局变量】,如第一行的rent。

在这个例子中,变量rent是在函数外被赋值的,所以它是全局变量,能被sum_cost()函数直接使用。

而变量variable_cost是在cost()函数内定义的,属于局部变量,其余函数内部如sum_cost()无法访问。事实上,当cost()函数执行完毕,在这个函数内定义的变量都会"消失”。

那要怎么解决“局部变量”和”全局变量“之间的矛盾呢?有几种方法可供参考,第一种方法最取巧:把局部变量都放在函数外,变成全局变量。还是以上面的代码为例:

10 函数:

那有没有一个能在函数内修改的方法呢?这时候global语句就能派上用场了,它可以将局部变量声明为全局变量,仔细看第三行代码。

10 函数:

global语句一般写在函数体的第一行,它会告诉Python,“我希望variable_cost是个全局变量,所以请不要用这个名字创建一个局部变量”。所以sum_cost()函数内部现在可以直接使用声明后的全局变量variable_cost。

函数的嵌套:

事实上,函数的嵌套我们并不陌生,最简单的例子就是print(len('我和你')),这里就是print()嵌套了len(),Python会先执行len()函数,得到一个返回值,再由print()打印出来。

rent = 3000

def cost():
    utilities = int(input('请输入本月的水电费用'))
    food_cost = int(input('请输入本月的食材费用'))
    variable_cost = utilities + food_cost
    print('本月的变动成本是' + str(variable_cost))
    return variable_cost

def sum_cost():
    sum = rent + cost()
    print('本月的总成本是' + str(sum))

sum_cost()
10 函数:

cost()函数运行结束后会返回变量variable_cost,而sum_cost()函数内部调用了cost(),所以调用sum_cost()的时候,cost()函数也会执行,并得到返回值variable_cost。

sum = rent + cost() 的结果就是sum = rent + variable_cost。最后调用函数时,也只需调用sum_cost()即可。

接下来我们再来看个例子:计算完成本之后,就是数数赚了多少钱的时候了!假设你想算出这个月的利润增长率,公式应该是:本月利润增长额 / 上月利润 * 100%

因为除数不能为0,为了使程序不报错,我们可以加进一些异常处理机制:当除数为0的时候重新输入数值。

10 函数:

可以看到,div()函数会计算并返回利润率,warning()函数是打印出一句提示,它们都被嵌套在main()主函数里,当满足不同的条件时,这两个子函数会被调用。

所以虽然定义了三个函数,但最后只需调用main()函数即可。

这里的调用语句是main(),所以会直接执行main()函数里的代码,我们可以结合注释来看下这段代码的执行顺序:(按箭头所标的序号)

10 函数:

课后练习:

眼看要过年了,深夜食堂经营的不错,你打算给员工发奖金犒劳一下。请你定义函数,当输入员工姓名和工作时长两个参数,即可打印出该员工获得多少奖金。发放奖金的要求如下:

工作时长不满六个月,发放固定奖金500元。

工作时长在六个月和一年之间(含一年),发放奖金120元*月数(如8个月为960元)

工作时长在一年以上,发放奖金180元*月数 (如20个月为3600元)

要求:使用两个函数,一个奖金函数,一个输入信息并打印函数

代码:

# 年终奖有多少?
def bonus(month):
    if month < 6:
        money = 500
        return money
    elif 6 <= month <= 12:
        money = 120 * month
        return money
    else:
        money = 180 * month
        return money
def info(name, month):
    gain = bonus(month)
    print('%s来了%s个月,获得奖金%s元' % (name, month, gain)) 
info('小米',21)

执行结果:

10 函数:

后续补充:

函数说明文档

10 函数:

按住 Ctrl(command)可以追溯到函数定义的位置,也可以在定义的位置查看到 help 描述的内容,下面以 print 函数为例

10 函数:

函数的实参和形参

  • 实参:实际参数,是在函数调用时,括号中出现的数据称为实参
  • 形参:形式参数,是在函数被调用时,接受函数调用传递过来的实参数据
  • 实参和形参在传递时要一一对应
10 函数:

此函数执行步骤:

  • 函数定义:在调用函数之前,该函数必须先被定义。
  • 调用处调用,跳转到函数定义位置执行函数,(1、参数传递:赋值 形参name 为 实参Tom 2、执行函数体,打印 hello,Tom 3、return返回值,这里没有数返回)
  • 返回到函数调用的位置,继续执行调用者的代码
10 函数:

函数返回值

一个函数,无论在哪遇到 return,那么这个函数都会直接结束执行,回到调用处。return 后面可以没有数据,也可以没有 return,默认返回为 None

遇到return返回到调用处证明以及 return 后面没有数据返回查看:

10 函数:

将一个函数返回值结果赋给一个变量,类似于 get_num()调用后,为其赋值给 1

10 函数:

函数要求:从键盘上获取(不需要传入参数)一个数字

强调一点:input() 函数的陷阱

10 函数:

这个报错信息表示你在尝试用除法运算符 (/) 对一个字符串 (str) 和一个整数 (int) 进行运算,这是不被支持的。此时 get_num()返回值类型为字符串类型

修改后:

10 函数:

函数要求:从键盘上传入两个参数,并计算参数累加和返回

10 函数:

函数包含多个 return 语句

一个函数中可以存在多个 return,但是只能有一个语句有效,在执行顺序上,第一个遇到 return 有效

10 函数:

发布者:LJH,转发请注明出处:https://www.ljh.cool/7771.html

(0)
上一篇 2022年9月12日 下午2:21
下一篇 2022年9月12日 下午9:38

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注