python编程入门十一:函数入门

第十一章:函数

讲到【函数】这一概念,想必大家都不会陌生,我们学过‘一次函数’、‘二次函数’、‘三角函数’等等;但计算机语言中的函数并非数学中的函数,但它们却有着差不多的“性质”,比如在计算机语言中,我们将【函数】解释为‘能够实现某种特定功能的代码的集合’,首先【函数】是一系列代码的集合,另外每个【函数】都是为了实现某种功能而存在的;这两点不难理解,其实数学中的【函数】依旧有这种性质,比如‘一次函数’是为了实现求一个指数幂为1的未知数而存在的,‘二次函数’则是为了求指数幂为2的未知数;所以,函数的概念一定要理解。

11.1 创建一个函数及调用

接下来我们直接亲手创建几个属于自己的函数,比如我们创建一个可以一次性说三行话的函数,函数同变量一样,都需要有自己的名字,这样我们在拥有多个函数的时候才不至于混淆;那这个函数我们取名为TalkBoy,我们尝试将它创建出来,创建一个函数需要用到def关键字:

def TalkBoy():
	print("Hi,my name is TalkBoy!")
	print("How are you?")
	print("Fine, thanks and you?")

结果:

Process finished with exit code 0

看到结果,你可能感觉有些奇怪,输出结果空空如也,这是什么情况;其实上面的代码我们只是创建了这个名为“TalkBoy”的函数,程序中它已经存在了,但是并没有调用它,那怎样进行调用呢?很简单:

def TalkBoy():
	print("Hi,my name is TalkBoy!")
	print("How are you?")
	print("Fine, thanks and you?")

TalkBoy()

结果:

Hi,my name is TalkBoy!
How are you?
Fine, thanks and you?

我们可以发现,调用一个已经创建的函数其实非常简单,只需要将它的函数名写出然后后面加一对括号就可以了,不难理解;函数的创建很复杂,但是函数的调用就很简单,就好比将函数比喻成一台汽车,函数的调用就是开汽车,打造一台汽车肯定是相对复杂的,但是开车就比较容易了。
但还会有人有这样的疑问:我为什么费这么大劲儿先创建函数然后调用函数,难道只编写函数里的代码不更简单吗?同样是可以实现这个功能的啊,没错,有这样的疑问很正常,就像是下面的两段代码:

#--------使用函数----------
def TalkBoy():
	print("Hi,my name is TalkBoy!")
	print("How are you?")
	print("Fine, thanks and you?")

TalkBoy()


#--------不使用函数----------
print("Hi,my name is TalkBoy!")
print("How are you?")
print("Fine, thanks and you?")

结果:

Hi,my name is TalkBoy!
How are you?
Fine, thanks and you?

Hi,my name is TalkBoy!
How are you?
Fine, thanks and you?

通过结果我们看到,确实如此,我们使用函数和不使用函数所输出的效果是一样的,那我们为什么还要使用函数呢?
其实多使用函数是一个好的习惯,为什么这么说?因为在大型的项目中,肯定会有特别多的项目功能,每个功能如果都清一色的写在主程序中,而不使用任何函数,那么如果突然某个功能出了bug,你怎样去定位这个功能在哪个位置呢?总不能通篇十几万行的代码逐一改bug吧!所以如果我们将每种功能都写进函数里,并且每个函数都注释的非常清晰,这样不管是在查bug还是项目交给同事分担的时候都会非常的方便;刚好在前几天红警的开发团队将红警95的代码放出来了,简直是代码规范的样板:
在这里插入图片描述
当然还有一个我们能感受到的原因,如果我们不使用函数来重复输出三次“TalkBoy”的话,可能只能采用下面比较笨的方法:

print("Hi,my name is TalkBoy!")
print("How are you?")
print("Fine, thanks and you?")

print("Hi,my name is TalkBoy!")
print("How are you?")
print("Fine, thanks and you?")

print("Hi,my name is TalkBoy!")
print("How are you?")
print("Fine, thanks and you?")

当然如果你能活学活用,把之前学过的for循环拿过来用,就变成:

for i in range(3):
	print("Hi,my name is TalkBoy!")
	print("How are you?")
	print("Fine, thanks and you?")

可能觉得已经挺简单了,但还是不如使用调用函数来的方便:

def TalkBoy():
	print("Hi,my name is TalkBoy!")
	print("How are you?")
	print("Fine, thanks and you?")

TalkBoy()
TalkBoy()
TalkBoy()

也许你会觉得函数并没有使用for循环简单,但使用函数会更加灵活,我可以选择在不同的位置调用TalkBoy函数,从而让它在不同情况下输出,因此,请你相信“得函数者得永生!”

11.2 函数的参数

参数,也叫参变量,是一个变量。我们在研究当前问题的时候,关心某几个变量的变化以及它们之间的相互关系,其中有一个或一些叫自变量,另一个或另一些叫因变量。如果我们引入一个或一些另外的变量来描述自变量与因变量的变化,引入的变量本来并不是当前问题必须研究的变量,我们把这样的变量叫做参变量或参数。英文名:Parameter。
看完之后是不是顿时失去了解“参数”的兴趣了,这是我在网上摘抄的关于参数的解释,说实话看完这些话我也没怎么弄明白,其实我们并没有必要“死扣”什么是参数,我们先学会怎样去使用,然后慢慢的去理解就可以了。

11.2.1 形参和实参

从“调用”的角度来看,我们可以把参数分为形式参数实际参数在一个函数中,参数位于哪个位置?我们回去观察TalkBoy函数,其实里面是没有参数的;那么接下来我们定义一个用于计算两个数字加法的函数Sum:

#--------加法函数----------
def Sum(x, y):
	z = x + y
	print(z)

Sum(1,2)

结果:

3

通过上面的代码,我们来区分一下形参实参形参指的是在创建一个函数时小括号里面的参数,也就是上面的(x,y),实参则代表在函数被调用时传递进来的参数,也就是上面的(1,2);我们在调用Sum函数时,会将实参1和2分别赋值给形参x和y,所以形参在这里其实更像是一个用于存储实参的“容器”。

11.2.2 关键字参数

关键字参数是什么?它有什么存在的意义?他肯定是为了解决某一问题而生的,通常在调用一个函数的时候,我们稍微一粗心就容易弄乱参数的位置,特别是当我们定义的函数它的形参非常多时,因为我们在传入参数时,系统会默认按照顺序将实参赋值给形参,一旦位置弄混,就会出现问题,因此关键字参数出现了,我们看下面的例子:

def CallBaby(call, baby):
	print(call + baby)

CallBaby("大哥","叫我")

结果:

大哥叫我

你看,我本来想让它输出“叫我大哥”,但由于我把位置弄反了,所以就输出了一个错误的结论,那接下来我们尝试使用关键字参数来解决一下:

#--------使用函数----------
def CallBaby(call, baby):
	print(call + baby)

CallBaby(baby = "大哥",call = "叫我")

结果:

叫我大哥

我们可以观察到,虽然传入实参的位置依旧是“大哥”“叫我”,但我们还是正确的输出了“叫我大哥”,所以关键字参数可以完全忽略参数的位置,更准确的输入正确的参数。

11.2.3 默认参数

默认参数“人如其名”,就是我们啥实参都不传入的情况下,它依旧有一个默认值,如果普通参数不传入实参的话,是肯定会报错的:

def defaultParams(param):
    print(param)

defaultParams()

结果:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    defaultParams()
TypeError: defaultParams() missing 1 required positional argument: 'param'

看吧,系统提示我们没有给param传入实参,但是我们如果使用默认参数就不会有这个问题,来看一下默认参数的用法:

def defaultParams(param='HaHa'):
    print(param)

defaultParams()

结果:

HaHa

果然如此,使用默认参数之后,即使粗心的我们忘记了向函数内传入实参,也不会被报错,这就相当于为我们的代码提供了一层保障。

11.2.4 收集参数

收集参数是个什么东西?我们只听说过收集邮票,收集手办,但我们为什么要收集参数呢?发明这个机制的人肯定也是为了解决某类问题,我们上面学习了函数的参数,我们可以向函数内传入一定数目的参数,你有没有想过,如果我们需要传入的参数数目不确定时该怎么办呢?这就需要用到收集参数了,收集参数的使用非常简单,只需要在创建函数的括号里的参数名前加一个(*)就可以了,看一下它的用法:

def multiParams(*params):
    print("当前拥有的参数数量为:"+str(len(params)))
    print("它们分别为:")
    for i in range(len(params)):
        print(params[i])

multiParams('p','a','r','a','m','s')

结果:

当前拥有的参数数量为:6
它们分别为:
p
a
r
a
m
s

我们发现,我们通过收集参数可以向函数内传入任意数量的实参,其实,我们通过收集参数这一机制会向函数内传入一个元组,元组内包含所有的实参,因此采用len()方法可以获得元组的长度,并且可以通过for循环来遍历输出每个实参;那普通参数和收集参数可以同时存在于函数的括号中吗?可以也不可以,为什么这么说?因为它们间的共存是分情况的。
收集参数前要带一个(*)是有含义的,收集参数只会收集(星号)之后的实参,也就是说我们如果在收集参数前加入若干普通参数是没有任何问题的:

def MultiParams(first,*params):
    print(first)
    print(params)

MultiParams(1,2,3,4,5)

结果:

1
(2, 3, 4, 5)

结果很显然,我们只将“1”传给了参数“first”,而把后面的数传给了收集参数“params”;如果你还有疑问的话,那就让例子再明显一些:

def MultiParams(first,second,third,*params):
    print(first)
    print(secend)
    print(third)
    print(params)

MultiParams(1,2,3,4,5)

结果:

1
2
3
(4, 5)

这样你对此肯定就不会有疑问了,收集参数确实只收集了(*)之后的内容,而不会影响到前面的参数;那此时,另一个疑问产生了,我们如果在收集参数的后面又加了普通参数会怎样呢?让我们实践出真知:

def MultiParams(first,second,*params,third):
    print(first)
    print(secend)
    print(params)
    print(third)

MultiParams(1,2,3,4,5)

结果:

Traceback (most recent call last):
  File "test.py", line 7, in <module>
    MultiParams(1,2,3,4,5)
TypeError: MultiParams() missing 1 required keyword-only argument: 'third'

我们发现,系统直接给我们报错了,丝毫不留情面,并且报错提示我们“MultiParams() missing 1 required keyword-only argument: 'third’”,我们在传入实参的时候遗漏了“third”这个参数,其实我们并没有忘记它,只是后面的参数都被params掳走了;那我们有办法在收集参数的后面再设置参数吗?可以,这里就需要用到默认参数了,同样观察下面的例子:

def defaultParams(first,second,*params,third = '7'):
    print(first)
    print(second)
    print(params)
    print(third)

defaultParams(1,2,3,4,5,6)

结果:

1
2
(3, 4, 5, 6)
7

我们通过观察结果发现,起码没给我们报错,而且确实将3之后的实参全部传给params了,而且third拥有自己无法被改变的实参“7”,所以我们得出结论:普通参数只能位于收集参数之前,而若要在其后添加参数则需要用到默认参数
讲到这,不要以为收集参数就讲完了,我们可以在参数前加一个(*)来形成一个可以传入多个实参并打包成元组的收集参数,当然还可以在参数前加两个(**)来形成另一种收集参数;关键字参数还没忘吧,就是在传入实参的时候标注好形参的名称:

def KeyWordParams(first,second):
    print(first)
    print(second)

KeyWordParams(first = '1',second = '2')

结果:

1
2

我们说,通过一个“ * ”可以把实参打包成元组,那么通过两个“ ** ”则可以把关键字参数打包成字典,我们不妨试一下:

def KeyWordParams(**params):
    print(params)

KeyWordParams(first = '1',second = '2')

结果:

{'first': '1', 'second': '2'}

结果显而易见,前面有两个(**)的收集参数把我们传入的关键字参数打包成了字典的形式,这样我们就可以活学活用这个工具了:

def UseParams(first,second,third = 3, *params,**Keyparams):
    print(first)
    print(second)
    print(third)
    print(params)
    print(Keyparams)

UseParams(1,2,4,5,6,one = 1, two = 2)

结果:

1
2
4
(5, 6)
{'one': 1, 'two': 2}

怎么样,上面的例子把我们所说的所有收集参数的情况都包含了,如果这个能理解了,那就不用头疼收集参数了;接下来我们头疼另外一种参数。

11.2.5 分配参数

分配收集,有没有在字意上发现了规律?每个概念的名字都不是随便取的,确实会符合他的字面意思,分配和收集是一对“反义词”,那这两种参数的功能也可以说是相反的。
讲到收集分配,不如先提一下另外两个概念:打包解包,我们在上面学习收集参数时,它的作用是将我们扔到调用函数的括号里的一堆实参打包成一个元组,把多个数据收集到一个“容器”里,我们称之为打包,那解包显然就是相反的含义了:把“容器”里的数据全部分配出来,其实也不是很令人头疼对吧!
那么为什么会发明分配函数这个机制呢?我们上面在使用收集参数的时候已经知道,我们可以将多个实参打包成收集参数,但我们并没有尝试过将一个元组打包成收集参数,这个想法很奇怪对不对?元组本来就是一个“包”了,那如果再把它打包成收集参数呢?这里就用到分配参数了,我们需要先把元组解包成多个元素,然后再将元素打包成收集参数,不要觉得这个操作很奇怪,有些时候确实会需要先解包再打包的流程;不妨我们先试一下,如果直接将一个元组传给收集参数会怎样:

def distribution(*params):
    print(params)
    
a = (1,2,3)
distribution(a)

结果:

((1, 2, 3),)

它并没有报错对不对?的确,这样的操作不会让系统觉得你有多奇怪,但是它并没有给出我们想要的结果,而是把一个元组当成一个元素传给了params,这就造成了元组里面套元组的结果,那接下来我们再尝试用一下分配函数

def distribution(*params):
    print(params)

a = (1,2,3)
distribution(*a)

结果:

(1, 2, 3)

看出区别来没有,一个很小的区别,我只是在传入实参的位置加了一个(*),输出的结果就完全是我们想要的一个元组了,所以分配参数的用法很简单,只需要在调用函数时,在实参之前加一个“星号”就可以实现解包了,同样的操作也适合将字典解包出来,使用两个“星号”就可以了,就是这么easy!

11.3 返回值

其实返回值这个概念是没必要单独一节来讲的,因为我能讲的内容不多,但它却也是一个函数的“重要”组成部分,为什么“重要”二字我用引号引起来了?因为有些时候函数里也不需要返回值,先不要晕,我们暂且只谈python中的函数返回值;我们在定义一个函数的时候,肯定会先定义好它的功能,也就是我们需要什么,如果我们只需要让函数里的代码走一遍过场,而不需要任何的结果,那就不需要返回值,但我们如果要定义一个用于计算加法的函数,那我们需要的肯定就是计算出来的结果,这时候就需要用到返回值,在函数中,我们会使用“return”来返回当前函数中的返回值,先举一个不需要返回值的例子:

def NoReturn():
    print("打印就可以了")

NoReturn()

结果:

打印就可以了

上面的代码就没有使用到return来返回一个值,只是单纯的运行一遍就可以了,那接下来看一个有返回值的:

def sum(a,b):
    c = a + b
    return c

result = sum(2,3)
print(result)

结果:

5

这个sum函数作为一个求和的函数,我们首先先函数里传入两个实参,然后在函数内运算之后,通过return将得到的c返回给我们,然后我们将返回值赋值给变量result,这就是一个完整的函数运算过程。
关于返回值,我能讲的也就这么多,主要原因还是这个概念特别好理解,没有什么阔谈大论的必要,所以关于函数,还是要将参数的知识理解好。
讲到这,如果你认为关于函数的内容就完结了,那你就太看不起函数了,关于函数,还有两章的内容要讲,准备好一起头疼吧!

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洪博hopes

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值