02-python 基础语法知识-02-函数

02-python 基础语法知识-02-函数

总体 要讲的大纲内容 如下

  • 循环- for while
  • 流程中断 continue break
  • 逻辑操作 and ,or ,not
  • 函数
  • python中的异常处理

​ 今天 是 我们开始学习python基础语法知识 中比较重要的一个概念 函数 。有了函数可以让代码 写起来 更加方便 ,更加可以复用。

函数是什么?

​ 你可以认为是 一段代码段,只不过这段代码 写了一个小功能,你只需要 通过名称就可以获取 调用这个函数里面的代码段 。 有点抽象 ,来看一个例子,比如我想完成一个功能 ,这个功能 就是要计算两个数 的值。 这个计算两个数的值 就是一个小小的功能。 来我们尝试一下写一个函数。

def my_sum(a, b):
    print(f"a={a},b={b}")
    return a + b
>>> def my_sum(a, b):
...     print(f"a={a},b={b}")
...     return a + b 
... 
>>> my_sum(5,7)
a=5,b=7
12
	

我通过 mysum(5,7) 就可以 调用 , 函数下面的代码段 。 你是不是 觉得 这样也没有什么啊? 不用函数 也能实现这个功能啊? 为啥要写 个函数呢?

没有关系,你现在还不知道 函数的厉害的地方。如果 有这个疑问你先保留,之后听我慢慢解释。

函数定义

下面 说下如何定义一个函数 关键 def

def fun_name():
    pass

这里 def 空格 然后 跟函数名称 ,然后括号 ,最后 有冒号, 下面就要开始缩进 四个空格.

还记得 之前我们写的一些代码吗? 猜数字的游戏,打印9*9 乘法口诀表等。

咱们 可以用函数 来重新写一下

比如猜数字 那个游戏的代码,我可以用函数 就可以重新 修改一下,把代码 写到 函数体 内就可以了。

这里 难点 def 后面的函数 名称,最好的根据名称 知道这个函数的功能。我一直再说 函数的功能性,请好好体会一下,

猜数字 小游戏

from random import randrange


def guess_number():
    # 记录猜的次数
    count = 0
    rand_nums = randrange(0, 51)

    num = int(input("请输入你要猜的数字 [0-50]: "))

    while True:
        if num > rand_nums:
            print("猜的太大了。")
            count += 1
            num = int(input("请输入你要猜的数字 [0-50]: "))
        elif num < rand_nums:
            print('猜的太小了')
            count += 1
            num = int(input("请输入你要猜的数字 [0-50]: "))
        else:
            count += 1
            print(f'恭喜你,猜对了. 你一共猜了{count} 次')
            # 猜对了要终止 循环 条件
            break

打印乘法口诀表

def print_multiplication():
    """
    打印 9*9 乘法口诀表
    :return:
    """
    for i in range(1, 10, 1):
        for j in range(1, i + 1, 1):
            print(f"{i}*{j} = {i * j} ", end=' ')

        print()

注意:

​ 给函数命名 一定要 比较清晰, 不要出现 aaa , bbb ,ccc, ddd , x, y ,z 等这样的函数名. 首先 从语法角度来说

这样命名没有错误,可以正常 执行对应的功能,但是 如果这个代码,过了一个月,你在看这样的代码, 你完全不知道 这里 名字 是什么意思? 而你要不得不 去要读 函数的每一行代码,才知道 这个函数干了什么事情。

感觉是不是有点简单啊, 只是把代码 放到一个 函数定义体内就可以了。 但是 突然有一天,以打印 乘法口诀表为例 , 现在我不想打印那么多, 我只想要5*5 的口诀表, 你说这挺简单啊, 只要把 range(1,10,1) 换成range(1,6,1) 不可以了。

def print_multiplication():
    """
    打印 5*5 乘法口诀表
    :return:
    """
    # 这里改动了
    for i in range(1, 6, 1):
        for j in range(1, i + 1, 1):
            print(f"{i}*{j} = {i * j} ", end=' ')

        print()

if __name__ == '__main__':
    # 这里 调用函数
    print_multiplication()
    pass

上面 确实是可以的, 但是每一次都要 修改 函数体的代码,如果哪一天 有人说 我要 6*6 的乘法表, 你还要在改代码吗?

有一个更好的方法,就是参数,在函数里面可以定义参数。以上为例,我来改一下 这个函数

def print_multiplication(number: int):
    """
    打印number*number 乘法口诀表
    :return:
    """
    for i in range(1, number + 1, 1):
        for j in range(1, i + 1, 1):
            print(f"{i}*{j} = {i * j} ", end=' ')

        print()

然后 这样调用

if __name__ == '__main__':
    print_multiplication(6)
    print_multiplication(5)
    pass

这样就可以打印 6*6 乘法表了。 如果之后需要变化 打印的 尺寸 范围 ,我们只要改变 要 传入number 的值就可以了。 是不是很方便呢?

函数的参数传递

​ 说一下 函数的参数, 从上面的例子中,可以看到 有了参数 可以 让代码 更加灵活,方便使用,并且 可以支持不同人 对 打印乘法表的需求。

python中函数参数 有几种, 位置参数,关键字参数, 冗余参数(我自己起的名字),

位置参数

下面我定义一个函数

def fun(a,b,c,d):
    print(f"a={a},b={b},c={c},d={d}")

直接 传入 四个数字 ,就会分别按照顺序 一次赋值给 a b c d 这四个值, 所以 这个就是叫位置参数,按照位置赋值。 看下面的例子

>>> 
... def fun(a,b,c,d):
...     print(f"a={a},b={b},c={c},d={d}")
...     
>>> 
>>> fun(1,2,3,4)
a=1,b=2,c=3,d=4
>>> fun(1,10,10,4)
a=1,b=10,c=10,d=4
关键字参数

下面的 我用另一种方式 ,

传递参数, 参数名=数字 这种方式 ,赋值。 这种方式 就可以 不按照顺序 传递参数了。

>>> fun(b=1,c=10,a=2,d=20) 
a=2,b=1,c=10,d=20

我能不能 位置 和关键字 一起使用呢? 当然可以啦。

>>> fun(1,2,d=10,c=4)
a=1,b=2,c=4,d=10

这里有一个要求, 就是 关键字参数 一定要放在 位置参数的后面。 这样python 解释器 就明白如何对应传值,碰到位置参数 直接赋值,然后在赋值 关键字参数。

注意下面 这种情况 是不被允许的.

>>> fun(1,d=10,2,c=4)
  File "<input>", line 1
SyntaxError: positional argument follows keyword argument

原因就是 因为 你想 2 赋值给b , 但是python 解释器 并没有那么智能,它并不能理解你的想法。 从报错的信息,可以看出, 位置参数 放在了 关键字参数的前面了 。所以这种会报错的,不能这样调用。

​ 你可能 有疑问,位置参数不香吗? 为啥要用关键字参数, 多写了一些代码, 没有必要。 其实原因 很简单, 想想 之前我有讲到 list 和dict 吗? list 虽然可以通过位置 找到元素, 但是 如果有很多元素,我很难记住每个元素的位置,如果 位置改变了,代码 又要改动呢? 而字典 就是通过key 来找到这个元素的, 这个key 的名称 是我们自己定义的, xiaoming ,xiaozhang,xiaowang 这样通过key 就可以找到这个元素了。我们 可读性就会很高。

这里我再次强调一下, 随着代码 量的增加,一定要主要一些变量的命名,尽量 知道它的含义。

好 ,言归正传, 关键字参数 也是这个作用,我不需要 去关注 位置,我只要对应参数 给上对应的值就可以了。 这个就是关键字参数的意义。

冗余参数

看下面的例子, 这个函数有两个参数

def fun(a,b):
    print(f"a={a},b={b}")

正常这样调用 没有任何问题。

>>> fun(1,2)
a=1,b=2
>>> fun(1,b=2)
a=1,b=2

但是 假设 有一天 不知道是谁 的粗心,传入了 超过两个的参数.

>>> fun(1,2,3,4)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: fun() takes 2 positional arguments but 4 were given

显然这里 报错,这里报错信息,说我需要两个参数,而你却给我4个参数。 所以你给的太多,我不知道怎么办啊? 所以我选择报错 。

为了兼容这种情况, 如果传入多了 ,只取前面对应的参数 ,后面的参数忽略,就行。这个就是需要冗余参数来解决这个问题.

在参数列表里面,加入 *args 这样 发现 函数就可以正常运行了。

>>> def fun(a,b,*args):
...     print(f"a={a},b={b}")
...     
>>> fun(1,2,3,4)
a=1,b=2

我们参入的3,4 这两个参数 去哪里呢? 让我来一探究竟

>>> 
... def fun(a,b,*args):
...     print(f"a={a},b={b}")
...     print(f'args:{args}')
... 
>>> 
>>> fun(1,2,3,4)
a=1,b=2
args:(3, 4)

>>> fun(1,2,3,4,5,6,7)
a=1,b=2
args:(3, 4, 5, 6, 7)

我打印args 发现了 3,4 这两个参数的值。原来 3,4 被赋值到 args 这个元祖类型的变量 上面。

好了 现在 这个函数 好像 已经很完美了,可以按照 位置或者关键字传入参数,用args 可以 吸收 传入太多的参数,并保存在元素里面。

来看下 下面的调用方式,我传入了一个关键字参数 c=5

>>> 
... def fun(a,b,*args):
...     print(f"a={a},b={b}")
...     print(f'args:{args}')
... 

>>> fun(1,2,3,4,c=5)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: fun() got an unexpected keyword argument 'c'

你想 这个肯定报错啊, 因为 如果要传入关键字,只能传入a =xx,b =xx, 你传入 c=xx, python解释器 当然不知道 c=5 这个参数怎么办, 所以 解释器 又报错了, 你得到 一个不期待的关键字参数c 。

你可能 会吐槽,这不是有病吗? 我都没有定义 这样的参数c,你就传进来,报错是正常的。

但是 有时候 程序员 要承受很多,要明白 有时候人没有那么聪明,或者其他人 没有仔细看你的代码参数的要求 。

如果要解决这种问题 ,怎么办呢? 其实 和刚刚方法类似的。

在函数定义的时候 加一个 **kwargs 这样如果有多余的关键字参数 传进来,就会被赋值到这个 字典中

看下面的例子

>>> def fun(a,b,*args,**kwargs):
...     print(f"a={a},b={b}")
...     print(f'args:{args}, kwargs:{kwargs}')
...     
>>> fun(1,2,3,4,c=5)
a=1,b=2
args:(3, 4), kwargs:{'c': 5}

>>> fun(1,2,3,4,c=5,f=6)
a=1,b=2
args:(3, 4), kwargs:{'c': 5, 'f': 6}

​ 我传入 多余的关键字参数 c, f ,这些值 会被 赋值到 kwargs 这个参数里面,这个参数是一个 字典。

看这样是不是就完美了, 我可以 吸收多余的位置参数, 多余的关键字参数,这个函数 好像兼容性 更好了。

所以程序员 是不是 要默默承受很多 ,这里要好好抱住自己,我不该承受那么多。 哈哈哈。

总结一下:通过 *args, **kwargs 来吸收多余的位置参数 和 关键字参数 ,在一定程度上保证了代码的容错能力。

仅仅支持关键字参数

如果你想定义的函数, 只希望传入关键字参数, 可以 * 然后定义 参数名称,这样的话, 就不能用位置参数 传入了。

注意: 在python函数定义里面,*后面的参数 一定要以关键字的形式 传入进来,否则就会 报错。

>>> def fun(*, name, hobby):
...     print(f"name={name},hobby={hobby}")
...     

>>> fun('frank','swim')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: fun() takes 0 positional arguments but 2 were given
    
>>> fun('frank',hobby='swim')
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: fun() takes 0 positional arguments but 1 positional argument (and 1 keyword-only argument) were given
>>> fun(name='frank',hobby='swim')
name=frank,hobby=swim

函数的返回值

函数 可以理解成 一个 拥有参数, 完成一定功能的代码段,并且这个代码段可以通过 函数名称() 来调用 ,

调用这个函数,函数 总要给调用者一个结果吧,这个结果 我们叫 返回值

现在 来一个简单的累加

获取1 + 2 + …+n 求和 完成 函数功能的编写.

很简单的函数,我就直接写了。

def my_sum(n):
    """
    求 1 +2 +...+n
    :param n:
    :return:
    """
    result = 0
    for i in range(1, n + 1, 1):
        result += i

    print(f"result={result}")

>>> 
... def my_sum(n):
...     """
...     求 1 +2 +...+n 
...     :param n: 
...     :return: 
...     """
...     result = 0
...     for i in range(1, n + 1, 1):
...         result += i
... 
...     print(f"result={result}")
...     
>>> my_sum(10)
result=55
>>> my_sum(5)
result=15

看起来功能正常的,也可以打印出 结果。

让我 试一下 ,调用一下这个函数看下。

image-02-02-function-01

结果如下:

image-02-02-function-02

结果 发现 results 为空, 并不是 55 ,而第一行打印 而是函数体里面的打印结果, 所以说这样调用 也没有问题,为啥没有结果呢?

其实原因就是 这个函数没有 把 result 返回给 调用方。

所以如何让函数 返回一个值呢? 只要加上 return xxx 就代表函数结束了,并且 返回了。

所以 修改一下代码

def my_sum(n):
    """
    求 1 +2 +...+n
    :param n:
    :return:
    """
    result = 0
    for i in range(1, n + 1, 1):
        result += i
    # print(f"result={result}")
    # 把结果返回回去
    return result

if __name__ == '__main__':
    results = my_sum(10)
    print(f"results={results}")
    pass

这个时候 就有结果了, 这个 就是返回值的意思,你要明白 如果没有显示的return 语句,函数只会一个None,这样调用方 就没有拿到这个已经计算好的结果, 所以这个函数 辛辛苦苦 写了 很多计算工作,结果没有把结果 return 回去 ,这样比较恐怖的。

这里我这样解释 不知道 你理解了吗? print , 和return 的区别, print 只是让我看到的结果,如果要把结果传入到调用方,需要我们显示的 return语句。 这就是 return 语句的作用 。

匿名函数

我更喜欢用 lambda 表达式 , 而不是用匿名函数 。 有的时候我们发现 我们不想定义一个函数,因为这个函数的复用性不强,还有这个函数比较简单,这个时候 我们 可以使用 lambda 来创建一个 函数

python 使用 lambda 来创建匿名函数。所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。

lambda [arg1 [,arg2,.....argn]]:expression

lambda 后面 跟着 函数参数,然后冒号后面是 一个表达式,这个表达式 的值 作为 这个函数返回。

用法,调用 和 函数一样,只是这个函数没有名字,我们把这个表达式赋值 给 f 。

>>> f  = lambda x,y:x+y 
>>> f(1)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: <lambda>() missing 1 required positional argument: 'y'
>>> f(1,2)
3
>>> f(1,2)
3
>>> f(10,20)
30

你可能觉得这个没有啥用, 我刚刚举例 没有看出这个lambda 表达式的好处,所以你觉得 没有啥用。 之后再比较 复杂的 场景中 我们会用到这个 lambda 表达式,你现在先记住这个 语法, 会有用的。

函数的类型是什么?

现在 我们来看下 函数是什么类型? 还记得用什么方法查看一个 类型吧,type(元素)

看例子:

>>> type([1,2,3])
<class 'list'>

>>> def fun():
...     pass
... fun
>>> fun
<function fun at 0x0000019D28045288>
>>> type(fun)
<class 'function'>

其实函数 和 基础的数据类型一样 , 也是一种 函数类型 。 注意看 类型 前面有个class 代表这是一个类, list 的类型 也是一个类。 其实 在python中 函数,一些基础类型都是类, 由类变成一个对象 。 所以 在python里面

基础的数据类型其实都是对象, 然后函数也是。

这些类型 基础类型,包括数字,字符串 都是由 类 生成的 。 由类生成的东西,我们 叫它 对象 . 你可能 对这个概念还比较模糊,没有关系, 你现在大概知道 这些基础类型 生成的东西,都可以叫做对象。以后我会慢慢 讲解 什么是对象。 这其实是一种编程思想,面向对象编程,今天只是 提一下,了解即可。

>>> type(list())
<class 'list'>
>>> type(tuple())
<class 'tuple'>
>>> type(dict())
<class 'dict'>
>>> type(set())
<class 'set'>
>>> type(str())
<class 'str'>
>>> type(frozenset())
<class 'frozenset'>
>>> type(1)
<class 'int'>

总结

​ 今天 我们 主要学习了 函数,如何用函数让你的代码更加的提高可读性,可复用性。 知道函数函数如何传递参数,如何 规定只传递关键字参数,函数返回值等, 还有lambda 表达式 了解。 以及知道函数 究竟是什么?

这些都是基础知识,这些是为我们之后 学习 写更大程序的一个基石 ,一定要好好掌握,多多练习,看看你有没有经常写的代码段 可以变成一个函数呢, 现在就开始行动吧。 加油!

参考文档

functions

分享快乐,留住感动. 2020-03-28 14:15:19 --frank
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值