4 Python 运算符、流程、函数

186 阅读22分钟

❤ 摘要

本章主要介绍Python的运算符以及优先级
以及流程控制语句
函数定义使用调用

知识点

  • 算术运算符

  • 比较运算符

  • 赋值运算符

  • 逻辑运算符

  • 位运算符

  • 成员运算符(in 和not in)

  • 身份运算符(s和is not)

  • 运算符优先级


  • 三大流程 if,while、 for
  • 函数(参数、返回值和作用域、匿名函数)

  • 自己编写的帮助文档 - 文档字符串

1. 运算符

1.1 算术运算符

算术运算符前面数字章节已经介绍过了,这里我们通过几个例子,再重新温习一下。

a = 21
b = 5
print(f'{a} + {b} = ',a + b)
print(f'{a} - {b} = ',a - b)
print(f'{a} * {b} = ',a * b)
print(f'{a} / {b} = ',a / b)
print(f'{a} // {b} = ',a // b)            #整除,向下取整
print(f'{a} 除以 {b} 的余数是 ',a % b)        #取模运算,返回 a 除以 b 的余数
print(f'{a} 的 {b} 次方是',a ** b)            #幂运算

运行效果如下:

图片描述


1.2 比较运算符

比较运算符是比较两个值,返回成立(True)或不成立(False)。

a = 6
b = 5
print(a == b)        #比较对象是否相等
print(a != b)        #比较两个对象是否不相等
print(a > b)        #比较 a 是否大于 b
print(a < b)        #比较 a 是否小于 b
print(a >= b)        #比较 a 是否大于等于 b
print(a <= b)        #比较 a 是否小于等于 b

运行效果如下:

图片描述


1.3 赋值运算符

以最简单的 = 来说,赋值运算就是把右边的值赋给左边。a = 5 不能认为是 a 等于 5,而应该是把 5 赋值给 a。

除了基本的赋值运算符之外,还有一些组合的赋值运算符,下面我们以例子说明。


加法赋值,a += b 等价于 a = a + b

a = 4
b = 2
a += b
print(a)

运行效果如下:

图片描述

减法赋值,a -= b 等价于 a = a - b

a = 4
b = 2
a -= b
print(a)

运行效果如下:

图片描述

乘法赋值,a *= b 等价于 a = a * b

a = 4
b = 2
a *= b
print(a)

运行效果如下:

图片描述

除法赋值,a /= b 等价于 a = a / b

a = 4
b = 2
a /= b
print(a)

运行效果如下:

图片描述

整除赋值,a //= b 等价于 a = a // b

a = 4
b = 2
a //= b
print(a)

运行效果如下:

图片描述

取模赋值,a %= b 等价于 a = a % b

a = 4
b = 2
a %= b
print(a)

运行效果如下:

图片描述

幂赋值,a **= b 等价于 a = a ** b

a = 4
b = 2
a **= b
print(a)

运行效果如下:

图片描述


:=海象运算符可以在表达式内部为变量赋值。

这是 Python3.8 新增的一个运算符。在下面的示例中,赋值表达式可以避免调用 len() 两次:

a='abcdefg'
if (b:=len(a))>3:
    print(f'字符串 {a} 的长度是 {b}')

运行效果如下:

图片描述


1.4 逻辑运算符

Python 也支持逻辑运算符 或(or)与(and)非(not)

  • 需要注意的是,在 Python 中,0None''[]{}() 等空值为假(False),其他为真。

或运算(or)

或运算(or),当左边的值为真则返回左边,否则返回右边的值。

a = 10
b = 20
a or b
0 or b

运行效果如下

图片.png


与运算(and

与运算(and),当左边的值为假则返回左边的值,否则返回右边的值。

a = 10
b = 20
a and b
0 and b

运行效果如下:

图片描述


非运算(not),当值为真(True)则返回假(False),当值为假(False)则返回真(True)。

a = 5
b = 0
not a
not b

运行效果如下:

图片描述


1.5 位运算符

位运算是直接对整数的二进制形式进行操作。我们先通过 bin() 函数查看对应数字的二进制形式,方便对比学习。

bin 查看二进制形式

bin(5)
bin(9)

运行效果如下:

图片描述

于是,我们得到了两个数的二进制形式,为两个数都补全八位,如下:

5 的二进制形式是 00000101

9 的二进制形式是 00001001


按位或(|

两个数的二进制形式的对应数位,只要有一个为 1 ,则该位为 1 。

前面我们已经得到了 59 的对应二进制,根据按位或的运算要求,我们很容易就可以手算出来 5 | 9 的二进制结果是 00001101,换算成十进制数字就是 13

图片描述

接下来,我们来看看直接通过 Python 进行计算。

5 | 9

运行效果如下:

图片描述


按位与(&

两个数字的二进制形式对应数位都为 1,则该位的结果为 1,否则为 0。

我们继续通过上面的 59 先手算一下,根据规则,很容易得出,5 & 9 的二进制结果是 00000001 ,转换成十进制就是 1

我们再来使用 Python 进行计算一下

5 & 9

运行效果如下:

图片描述


按位异或(^

按位异或是当两个数字的对应的二进制位不同时,结果为 1,否则为 0 。

我们继续通过 59 进行学习。

5 的二进制形式是 00000101

9 的二进制形式是 00001001

根据规则,我们很容易手算出结果是 00001100,转换成十进制是 12

使用 Python 直接计算,如下:

5 ^ 9

运行效果如下:

图片描述


按位取反(~

按位取反的值 ~x 相当于 -(x+1)

~5

运行效果如下:

图片描述


左移运算符(<<

左移运算符在运算时,通常是将所有二进制位左移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补0。

由于 Python 3 中整数没有大小限制,所以不存在高位丢弃的现象。在 Python 左移 n 位等价于乘以 pow(2, n),也就是乘以 2 的 n 次方。

5 的二进制形式是 00000101,左移两位,就是 00010100,转换成十进制就是 20

5<<2

运行效果如下:

图片描述

正好是 5*2**2 的结果,记住这点,计算更方便了。


右移运算符(>>

右移运算符在运算时是将所有二进制位右移若干位,由 >> 右边的数指定移动的位数,高位补0,低位舍弃。在 Python 中右移 n 位等价于除以 pow(2, n) ,作向下取整除法。

5 的二进制形式是 00000101,右移两位,就是 00000001,转换成十进制就是 1

5>>2

运行效果如下:

图片描述

正好是 5//(2**2) 的结果。


1.6 成员运算符(in 和 not in)

成员运算符就是判断某一对象是否在序列中,在就返回 True,否则返回 False。 前面数据类型的章节我们已经多次使用,下面我们举个例子,再次温习。

a = 6
b = [2,4,6,8]
a in b
'9' not in b

运行效果如下:

图片描述


1.7 身份运算符(is 和 is not)

身份运算符是判断两个对象是否指向同一内存地址,可以使用我们前面介绍过的 id() 函数判断对象的内存地址。

a = 1
b = 1
a is b
a is not b

运行效果如下:

图片描述

从上面的例子中你可能发现 is== 好像一样,但其实,并非如此。

== 比较的是两者的值,而 is 比较的是两者的内存地址

我们再来看一个例子

a = 956
b = 956
a == b
a is b
c = 2.1
d = 2.1
c == d
c is d

运行效果如下:

图片描述

是不是感觉有点奇怪,相同的数字,有时候内存地址相同,有时候不同。其实,这是因为 Python 解释器对 较小的整数进行了缓存

不同的编译器可能还会有意想不到的差别,所以在比较的时候慎重使用。


2 运算符的优先级

Python 中的运算符的 运算规则 是:

  • 优先级高的运算先执行,优先级低的运算后执行,同一优先级的操作按照从左到右的顺序执行(除了幂运算是从右至左分组)。

运算符的优先级从高到低大致符合下表,但不绝对:

运算符描述
**幂运算
~+-按位取反、正号、负号
*/%//乘、除、取余、整除
+-加法、减法
<<>>左移运算符、右移运算符
&按位与
^1按位异或、按位或
<=<>>===!=比较运算符
=%=/=//=-=+=*=**=赋值运算符
isis not身份运算符
innot in成员运算符
orandnot逻辑运算符

举例说明:

下面,我们首先看一些例子。

print(5 + 6 * 3 - 4 / 2)        #四则混合运算,先乘除,后加减,从左到右
print(5**2**3)        #幂运算从右往左分组计算,相当于是 5**(2**3)
print(5 + 3 * -1)        #负数的使用,相当于 5 + 3 * (-1)
print(6*3**2*2)        #先进行幂运算再计算乘法,相当于 6*(3**2)*2
print(1<<5-4)        #四则运算优先级高于位运算,相当于 1<<(5-4)
print(6-3>2+5)        #四则运算优先级高于比较运算符,相当于 (6-3) > (2+5)

运行效果如下:

图片描述

下面我们再来一个成员运算符和逻辑运算符的例子

print(6 and 0)            #与运算遇 0 则 0
print(0 in [0])            #成员运算符返回 True 或 False
print(6 and 0 in [0])        #成员运算符优先级高于逻辑运算符

运行效果如下:

图片描述


运算符总结

  1. 运算符

    • 算术运算符(+-*/%//
    • 比较运算符(<=<>>===!=
    • 赋值运算符(=%=/=//=-=+=*=**=
    • 逻辑运算符(orandnot
    • 位运算符(|&^~<<>>、)
    • 成员运算符(innot in
    • 身份运算符(isis not
  1. 运算符的优先级(由高到低)

3. 选择结构

流程控制也叫控制流程,通常是指命令和语句的执行顺序。因为有了流程控制,计算机才知道该如何运行一段程序。

通常,我们把流程控制分为三类:顺序结构、选择结构、循环结构。


3.1 if语句

选择结构分为 单分支双分支多分支

if 语句,属于单分支结构,若 if 语句中的条件成立,就会执行 if 语句后的代码; 若条件不成立,则跳过 if 语句后的代码段。单分支结构中的代码段只有“执行”与“跳过”两种情况。

a = 5
if a == 5:
    print('a 等于 5')

if a == 6:
    print('a 等于 6')

运行效果如下:

图片描述

可以看到,满足条件的会执行,不满足条件的不会执行。


3.2 if...else 语句

if...else 语句属于双分支结构,如果 if 语句中的条件成立,就会执行 if 语句后的代码,若条件不成立,则执行 else 中的代码。

a = 3
if a > 5:
    print('a 大于 5')
else:
    print('a 小于等于 5')

运行效果如下:

图片描述


3.3 if...elif...else 语句

在一些城市,会对老年人乘坐公交车进行优惠,比如 60 岁到 69 岁实行半价,70 岁及以上免费,60岁以下的人员则按原价。

根据上面的内容,我们可以写出大致流程如下:

如果年龄大于等于 60 岁且小于 70 岁:
    ->半价
如果年龄大于等于 70岁:
    ->免费
其他:
    ->原价

这是标准的选择结构,在 Python 中,我们可以使用多分支结构语句 if...elif...else 来实现上面的功能。

写入下面的代码(注意,缩进都是 4 个空格):

age = int(input("请输入您的年龄:"))
if 70 > age >= 60:
    print("半价")
elif age>70:
    print("免费")
else:
    print("原价")

写入文件之后我们就可以运行程序了,运行效果如下:

图片.png

  • if、elif、else 后要加英文半角冒号 :,告诉计算机这是一个判断语句,如满足条件,就执行语句块内部的代码。

  • Python 使用严格的缩进,来区分代码块的执行级别。比如 if 下面的代码要缩进四个空格,代表的是 if 内部的功能;else下面的代码同样要缩进四个空格,代表是else内部的功能。

  • 上面的例子中,我们在判断年龄时,使用了 70 > age >= 60 这样的语句,这和我们日常生活中的使用非常相似,也可以分开写成 70 > age and age >= 60

  • input() 接收用户的输入,类型为字符串 string,但字符串不能和整数比较大小,所以要用 int() 函数将数据转换为 int 整数类型。前面我们讲到过,int() 在接收字符串类型时,字符串中的数字必须是整数。


3.4 if 嵌套

有时候我们也需要在判断语句中进一步判断,比如上面的例子中,我们也可以换个思路。

如果年龄大于等于 60 岁:
    如果年龄大于等于 70 岁:
        ->免费
    否则
        ->半价
否则:
    ->原价

改写成 Python 语句就是:

age = int(input("请输入您的年龄:"))
if age >=60:
    if age>=70:
        print("免费")
    else:
        print("半价")
else:
    print("原价")

运行效果如下:

图片.png


3.5 pass空语句

pass 语句是一个空语句,不做任何事情,一般用做占位语句。当我们开发过程中,遇到暂时不确定的内容,可以使用 pass 占位,这样运行就不会报错了。

比如下面的例子中,我们暂时没有想到 else 中将要执行什么,我们就可以使用 pass 代替。

a = 10
if a>5:
    print('a 大于 5。')
else:
    pass

运行效果如下:

图片.png


当然,如果我们不需要使用 else 子句,也可以不写,等价于使用了 pass 语句,和前面 if 语句一样。

a = 10
if a>5:
    print('a 大于 5。')

运行效果如下:

图片描述


3.6 match...case 语句

在 Python 3.10 中,官方还新增了 match...case 语法,这也是一个多分支结构,该语法在其他一些编程语言中叫 switch

ps: 安装python3.10
linux安装:首先添加软件源: sudo add-apt-repository ppa:deadsnakes/ppa
然后就可以安装 Python 3.10 : sudo apt install python3.10 -y


安装好了 Python 3.10 ,我们继续学习 match...case 语句。首先看一个例子:

a = 3
match a:
    case 2:
        print('a 是 2')
    case 3:
        print('a 是 3')
    case _:
        print('a 不是 2,也不是 3')

运行效果如下:

图片描述

  • 最后一个 case 中的 _ 是通配符,就是匹配剩余的部分,有点类似 else。如果省略最后的 _,如果没有匹配到就会返回 None

4. 循环结构

Python 中包含两种循环方式,一种是 for 循环,一种是 while 循环。

4.1 for循环

Python 的 for 循环可以迭代列表或字符串等任意序列,元素的迭代顺序与在序列中出现的顺序一致。下面,我们先看一个例子。

a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for i in a:
    print(i)

我们在交互模式运行,运行效果如下:

图片描述

此处的 i 代表从列表中取出的元素,你也可以给它其他名字。


4.2 range 函数

如果需要处理一组数字列表,并且数字列表满足一定的规律,可以使用 Python 的内置函数 range()

使用 range(x) 函数,可以生成一个从 0x-1 的整数序列。比如我们想打印 0 到 9 的数,可以这样写:

for i in range(10):
    print(i)

运行效果如下:

图片描述


还可以用 range(a,b) 取某个区间的数,比如要打印 1 到 10 ,你可以写:

for i in range(1,11):
    print(i)

运行效果如下:

图片描述


也许你已经发现了,range() 函数和索引切片的操作非常类似,也是包头不包尾,同样,range() 函数也支持步长。

for i in range(1,10,2):
    print(i)

运行效果如下:

图片描述


前面我们需要打印 5 遍 hello 的例子,使用 for 循环配合 range() 函数将非常容易的解决。

for i in range(5):
    print('hello')

运行效果如下:

图片描述


另外,print 函数还有一个 end 参数,该参数默认值(也就是不加end时候)是回车换行,我们可以通过修改该参数实现更丰富的打印模式。

for i in range(5):
    print('hello',end=" ")

运行效果如下:

图片描述

因为行尾不换行,所以 Python 交互环境的提示符出现在了行尾。


4.3 while 语句

另外一种循环是 while 循环,它的语法和用途都和 for 循环有些不同。

for 循环适用于 已知循环次数 的循环,所以后面跟的是次数或一个可迭代对象,到达指定次数或者迭代结束就停止。

但现实情况是,我们往往并不知道需要循环多少次,而是希望满足某个条件再停止。就像抢票软件一样,你并不知道自己会在第几次抢到,所以就要不断的抢票,直到抢到为止。

while 循环可以帮我们解决这个问题。while 后面跟的是一个条件,只要条件满足,这个循环就会一直进行下去。

语法如下:

while 语法


下面,我们来用 while 循环,打印 1 到 10 的数字,亲手感受一下 while 循环有哪些不同:

a = 1
while a <= 10:
    print(a)
    a = a + 1

运行效果如下:

图片描述


这个程序是这样运行的:

  • 一开始,a 的预设值为 1。
  • 来到第二行的 while 循环,此时 a <= 10,满足条件,第一次进入循环。
  • 执行循环中的命令,打印 a 的值。
  • 进入第四行,把 a 的值加 1 ,一轮循环结束,此时会再判断 a 是否 <= 10,如果仍满足条件,则继续执行循环。
  • ......
  • 第 10 次循环时,执行第 3 行打印此时 a 的值 10。
  • 然后给 a 的值加 1,准备第 11 次循环。
  • 因为此时 a > 10 不满足条件,循环停止。
  • 一共执行了 10 次 循环。

再总结一下两种循环的区别:


4.4 break 和 continue

在循环中,我们可以使用 breakcontinue 这两个关键字,进一步控制流程。

break 表示停止当前循环

for a in range(10):
    if a == 5:
        break
    print(a)

运行效果如下:

图片描述

程序只打印了 0 到 4 ,当 a 等于 5 时,循环直接就停止了。


continue 表示跳过本轮循环,去执行下一轮循环。

a = 0
while a < 10:
    a = a + 1
    if a == 5:
        continue
    print(a)

运行效果如下:

图片描述

程序从 1 开始打印,当 a 等于 5 时,自动跳过,继续开始下一轮,所以最终打印了 1 到 10 中除 5 以外的其他数字。


  • 本章我们:

我们介绍了 for 循环和 while 循环,循环让程序可以重复操作,大大解放了人力。

有时候,我们程序写的有问题,导致条件一直满足,这就陷入了“死循环”,这时候,我们就需要使用 Ctrl + C 来结束程序。


流程控制总结

  1. 流程控制分为 顺序结构选择结构循环结构

  2. 顺序结构从上到下,依次执行,每条语句只执行一次。

  3. 选择结构分为 单分支双分支多分支

    • 单分支结构使用 if 语句。
    • 双分支结构使用 if...else 语句。
    • 多分支结构使用 if...elif...elsematch...case 语句。
  4. if 嵌套是指选择结构中还有选择结构的形式。

  5. pass 语句是一个空语句,不做任何事情,一般用做占位语句。在开发过程中,遇到暂时不确定的内容,可以使用 pass 占位,这样运行就不会报错了。

  6. Python 中包含两种循环方式,一种是 for 循环,一种是 while 循环。

    • for 循环可以迭代列表或字符串等任意序列,元素的迭代顺序与在序列中出现的顺序一致。
    • while 后面跟的是一个条件,只要条件满足,这个循环就会一直进行下去。
  7. break 表示停止当前循环,continue 表示跳过本轮循环,去执行下一轮循环。

  8. 陷入了“死循环”,我们可以使用 Ctrl + C 来结束程序。


5. 函数

5.1 函数的概念

函数是编程里最重要的概念之一。我们经常需要在程序里重复调用相同的代码,你不可能每次调用时都把代码整个复制一份,这样做既不利于阅读,也不利于维护。正确的做法是:把这段代码封装成一个函数,下次用到时调用这个函数即可。

  • 函数就是指一段可以直接被另一段程序或代码引用的程序或代码。

我们在前面章节其实已经多次接触函数这个概念,下面列举几个我们使用过的函数:

print():打印一段内容到屏幕;

input():接收用户输入的内容;

int():将数字或只包含整数的字符串转换为整数类型;

list():将一个数据容器转换为列表;

sorted():对可迭代对象进行排序,返回列表形式;

sum():求和函数,可对数字组成的可迭代对象求和。

上面的这些都是 Python 的“内置函数”,也被成为“内置方法”,都是我们最常用的功能,Python 帮我们写好了,需要时直接调用即可。


5.2 函数的调用

  • 所谓函数调用,就是指对函数的调取使用,简单来说就是运行这个函数。我们可以在其他函数内调用一个函数,也可以直接调用一个函数。

下面我们举个例子:

print("Hello World")
int(25.36)
list('hello')
sorted('story')
print(sum([1,2,3]))

运行效果如下:

图片描述

以上操作就是对函数的调用,括号内的内容叫做参数,函数接收参数,返回结果,这个结果被称为函数的返回值。有的函数没有返回值,只有输出值,比如上面的 print() 函数。


大型程序都是调用了非常多的函数组合而成的,其中每一个函数可能又调用了很多函数。

学习初期,我们要好好学习官方内建函数。能够调用现成的函数,实现功能,然后对这些不同的函数进行组合,就可以实现更强大的功能了。


5.3 内建函数(直接调用)

前面我们其实已经介绍过如何查看 Python 有哪些内建函数,接下来,我们再介绍一次。

其实很简单,在交互环境输入 dir(__builtins__) 就可以看到。

图片描述

另外,每一个函数的使用方法,我们可以通过内建的 help() 函数进行查看,比如我们查看 abs 函数的用法。

图片描述

可以看到,abs 函数是用来返回绝对值的,只有一个参数,于是我们就知道该如何使用了。

abs(5)
abs(-3)
abs(0)

运行效果如下:

图片描述

运行结果和我们想象的一样。

我们也可以通过这种方法学习更多的函数,另外,在学习过程中,搜索引擎是我们的好帮手,。


接下来,我们学习下如何定义自己的函数。


5.4 自定义函数-def

我们自己创建函数,被叫做用户自定义函数

定义函数需要注意以下几点:

  • 关键字 def 是告诉 Python,你要定义一个函数。
  • def 后面是函数名称和圆括号 (),后面加冒号(:
  • 函数名称应遵循标识符的命名规则。
  • 如果有需要接收的参数,需要放在圆括号内。
  • 注意函数内部代码的缩进,冒号后的所有缩进构成了整个函数体。

下面我们先定义一个函数,在交互环境输入下面的代码:

def hello():
    print('Hello World!')

然后我们就可以调用 hello() 函数了,其中 hello 是函数名,括号 () 是函数调用运算符。

运行效果如下:

图片描述

上面我们定义了一个简单的函数,每次调用函数,都会打印 “Hello World!”,随着学习的深入,我们会定义更加复杂的函数。

需要注意的是,在 Python 中,函数必须先定义才能调用,也就是说函数的调用应该在定义之后,如果顺序错了,就会有未定义的报错提醒。


5.5 函数的参数

形参和实参

我们都知道,print()input() 等函数的括号中,都是可以填写内容的,执行后可以对指定的内容进行打印、接收等操作。如 print('hello world'),就会打印指定的内容 hello world

在上节实验中,我们定义了一个 hello 函数,如果我们想要这个 hello 函数能够向不同的名字问好,就需要增加一个参数,比如我们定义为 name。就可以写出如下代码:

def hello(name):
    print(f'Hello {name}!')

接下来,我们来运行函数

hello('John')
hello('张三')
hello('李四')

运行效果如下:

图片描述

函数的参数分为 形参实参, 在上个例子中,定义函数中的 name 就是形参 ,调用函数时传递进去的 'John''张三''李四' 就是实参。`


✨顾名思义:

形参就是形式上的参数,可以理解为数学中的 x,并没有实际的值,相当于一个变量名;你也可以给它取其他名字,但最好是和参数意义接近的;

实参就是实际意义上的参数,在调用函数 hello('John') 的过程中,将实参 'John' 传递给了形参 name,并储存在 name 中。


默认值参数

上一小节,我们定义了一个可以指定参数的函数 hello,但如果我们忘记了指定参数,会发生什么情况呢?我们运行试试。

def hello(name):
    print(f'Hello {name}!')

hello()

运行效果如下:

图片描述

可以看到,这里提示我们缺少一个位置参数 name位置参数也叫必选参数,简单来说就是定义的时候设置形参的位置在调用的时候就必须有对应的实参。


为了避免忘记给参数赋值而报错的情况,我们可以为参数指定默认值,这样,在调用函数时,如果我们对默认参数没有赋值,则该参数保持为默认值;如赋值则改变为新值。

我们对上面的函数进行修改。

def hello(name="World"):
    print(f'Hello {name}!')

hello()
hello("Jobs")

运行效果如下:

图片描述


注意情况

  • 下面是两点需要注意的情况

默认不设可变对象

  1. 默认值应当设为不可变的数据类型(如 字符串元组数字布尔值None 等)。当默认值为 列表字典 等可变对象时,会产生与该规则不同的结果。

例如,下面的函数会累积后续调用时传递的参数:

def f(a,L=[]):
    L.append(a)
    return L

f(1)
f(2)
f(3)

运行效果如下:

图片描述

可以看到,列表 L 把之前的值都存储了。

解决方法,每次设none

对此,我们其实也有办法解决,修改函数如下:

def f(a, L=None):
   if L is None:
       L = []
   L.append(a)
   return L

f(1)
f(2)
f(3)

运行效果如下:

图片描述


顺序问题,默认参数后面不能再有位置参数。

顺序问题,默认参数后面不能再有位置参数。

我们举例说明,尝试定义下面函数:

def hello(name="World",word):
    print(f'{word} {name} !')

运行效果如下:

图片描述

报错了,默认参数后面不能有非默认参数

解决方法:顺序更改

正确的写法如下:

def hello(word,name="World"):
    print(f'{word} {name} !')

hello("Hi","Lucy")
hello("Hello")

运行效果如下:

图片描述


关键字参数 (不考虑顺序)

关键字参数是指在调用函数时直接对形参赋值,使用关键字参数的好处是不用在乎参数传入的顺序。

下面举个例子:

def person(name,age):
    print(f"{name}今年{age}岁了。")

person(name="张三",age=25)
person(age=50,name="李四")

运行效果如下:

图片描述

可以看到,参数 nameage 的顺序并没有影响函数的正常运行。


可变参数(数量可变)

可变参数也叫不定长参数,顾名思义,参数的数量是可变的,调用函数时,可以在可变参数的位置上,传入任意数量的参数。

以元祖tuple形式导入

定义可变参数的方法其中一种是在参数名前面加上星号 *,该参数会以元组(tuple)的形式导入,存放所有未命名的变量参数


比如我们实现一个求和的函数。

def total(first,*number):
    print(type(number))
    print(sum(number,first))

total(1,2)
total(1,2,3,4)

运行效果如下:

图片描述

运行正常,而且也验证了上面说的该参数以元组类型存放。

在调用函数的时候,如果没有指定可变参数的值,它将是一个空元组。

def total(first,*number):
    print(type(number))
    print(number)
    print(sum(number,first))

total(1)

运行效果如下:

图片描述


以字典dict形式导入

另外一种定义可变参数的方法是在参数前加上两个星号 **,该参数会以字典(dict)的形式接收关键字形式的引用。

def person(name,age,**other):
    print(f'姓名:{name}')
    print(f'年龄:{age}')
    print(f'其他信息:{other}')

person('王五',26,gender="男",job="作家")
person('赵六',28,gender='女',job="画家",city="北京")

运行效果如下:

图片描述


特殊参数

默认情况下,参数可以按位置或关键字传递给函数。

但有时候为了让代码易读、高效,我们会限制参数的传递方式,如仅按位置传递参数、仅按关键字传递参数、同时包含仅按位置参数和仅按关键字参数。


仅按位置传递参数

仅按位置传递参数的函数,其形参应该放在斜杠(/)前面。

def school(name,city,/):
    print(f"学校名称:{name}")
    print(f"所在城市:{city}")

然后尝试调用函数

school("浙江大学","杭州")
school(name="北京大学",city="北京")

运行效果如下:

图片描述

可以看到:
第一行调用成功了,因为我们只是在对应的位置放入了实参
第二行调用时我们使用了关键字参数的方式,此时就报错了,报错信息很容易看到“仅限位置参数”相关的提示。


仅按关键字传递参数

仅按关键字传递参数的函数,形参应该放在星号(*)后面。**

def school(*,name,city):
    print(f"学校名称:{name}")
    print(f"所在城市:{city}")

school("浙江大学","杭州")
school(name="北京大学",city="北京")

运行效果如下:

图片描述

这次第一行在尝试以位置参数调用时,函数报错,而第二行的关键字参数调用正常运行。


同时包含仅按位置\关键字参数

这种形式其实就是前面两种的复合模式。

def school(name,/,shortname,*,city):
    print(f'学校名称:{name}')
    print(f'学校简称:{shortname}')
    print(f'所在城市:{city}')

school("北京大学","北大",city="北京")
school("北京大学",shortname="北大",city="北京")

运行效果如下:

图片描述

斜杠前面只能是位置参数,星号后面只能是关键字参数,中间地带正好是两不管,也就是一般的参数,你可以自由选择任意方式调用。


5.6 函数的返回值和作用域

函数的返回值

前面我们曾经举过一个例子,展示 Python 内置函数的使用。

total = sum([1,2,3])
print(total)

运行效果如下:

图片描述

这里相当于把 sum([1,2,3]) 作为 print 函数的参数。而能够这么使用是因为 sum 函数有返回值。

我们前面虽然写了很多函数,但这些函数其实都没有返回值,也就是说,这些函数只能输出内容,但函数本身是没有值的。


为了更形象的理解,我们举个例子。

def hello():
    print('Hello World!')

我们定义了一个 hello 函数,当我们把函数赋值给一个变量,再反复调用这个变量会有什么效果呢?

a = hello()
a
a

运行效果如下:

图片描述

可以看到,在我们赋值的时候,函数其实就已经输出了结果,而继续调用反而没有任何内容。


那如果实现 sum 函数那样的效果呢?——增加一个return返回值

def hello():
    return 'Hello World!'

a = hello()
a
a

运行效果如下:

图片描述

这就实现了我们想要的效果,大家应该发现了,和之前不同的是,此处的函数中使用了一个 return 关键字。


  • return 关键字是给函数设置返回值的,方便其他地方调用,不设置 return 或者 return 内容为空都代表返回 None

  • 不设置 return 只是没有返回值,但不会影响函数的输出print() 函数就没有返回值。


变量的作用范围

Python 中有两种基本的变量作用域:局部变量全局变量

局部变量(local)

在函数内部定义的变量名只能被函数内部引用,不能在函数外引用,这个变量的作用域就是局部的,所以它就是一个局部变量。

def person():
    age = 20
    print(f'从函数内部访问变量 age 的值:{age}')

person()
print(f'从函数外面访问变量 age 的值:{age}')

运行效果如下:

图片描述

可以看到,我们在函数内部使用变量 age 没有问题,但在函数外面直接调用变量 age,就会提示“未定义”的错误。


全局变量(global)

隐式全局变量(函数外部变量)

  • 函数内部只作引用的 Python 变量会被隐式的视为全局变量。
a = 5
def x():
    print(a)

x()

运行效果如下:

图片描述

可以看到,虽然我们没有在函数内部定义变量,但因为我们只是引用变量的值,而不去改变它,所以这个变量可以正常被访问,这叫做隐式的全局变量。


函数内部声明 - global

  • 如果在函数内部任何位置为变量赋值,除非明确声明为全局变量,否则均将其视为局部变量。

下面我们定义了一个函数,在没有声明全局变量的情况下,尝试改变函数外面的变量值,就会报错。

a = 8
def x():
    a +=1
    print(a)

x()

运行效果如下:

图片描述

可以看到,此时函数内部的 a 属于局部变量,当尝试改变它的值时,就会报错。


此时,要想声明全局变量,我们就需要使用 global 关键字。

让我们修改上面的代码:

a = 8
def x():
    global a
    a +=1
    print(a)

x()
a

运行效果如下:

图片描述

此时,变量 a 就成了全局变量,我们成功在函数内部对其进行了修改。


需要注意的是修改全局的 不可变类型 才需要声明,像列表、字典这种可变数据类型,使用内置方法修改元素时,其对象并没有发生变化,不需要声明。


嵌套函数变量- nonlocal 变量

此处还有一个 nonlocal 关键字,但它只用在嵌套函数内部。嵌套函数就是函数内部的函数。

def outer():
    num = 1
    def inner():
        nonlocal num   # nonlocal关键字声明
        num = 2
        print(num)
    inner()
    print(num)

outer()

运行效果如下:

图片描述

可以看到,外层函数的 num 变量的值被内层函数给修改了。


5.7 匿名函数 -lambda

lambda函数的使用

在 Python 还有一种更灵活,更小巧的定义函数的方式,本节实验实验我们将介绍匿名函数 lambda 的使用。


匿名函数,顾名思义,这类函数没有函数名,使用匿名函数的可以减少代码量、使代码结构更加紧凑。我们使用 lambda 关键字创建匿名函数,因此也称 lambda 函数或 lambda 表达式。


下面我们首先通过一个例子认识匿名函数 lambda 的使用。

double = lambda x: x * 2
double(10)

运行效果如下:

图片描述

上面的例子中,我们使用 lambda 定义了一个匿名函数。 lambda 定义返回值时不需要 return 关键字,冒号后面就是返回值,lambda后面跟的是函数的参数。


Lambda 函数可用于任何需要函数对象的地方。

  • 在语法上,匿名函数可以有多个参数,但只能有一个表达式。

  • 在语义上,它只是常规函数定义的语法。

  • 与嵌套函数定义一样,lambda 函数可以引用包含作用域中的变量,但不能访问自有参数列表之外或全局命名空间里的参数。


很多时候,我们会把匿名函数作为其他函数的实参来使用,非常方便。

比如内置函数 sorted() 有一个可选的参数 key,用于配置排序的方式。

如果我们需要以每个元素的最后一位进行排序,如果使用一般函数,代码如下:

a = ['dog','abandon','cool','black']
def select(x):
    return x[-1]

sorted(a,key=select)

运行效果如下:

图片描述

改用 lambda 函数实现:

a = ['dog','abandon','cool','black']
sorted(a,key=lambda x:x[-1])

运行效果如下:

图片描述

可以看到,lambda 函数更加简洁。一般函数可能需要好几行才能实现的功能,lambda 函数一行就搞定了。


lambda 函数的其他示例

lambda 函数的参数是可选的

a = lambda:True
a()

运行效果如下:

图片描述

lambda 函数可以使用多个参数

a = lambda x,y:x+y
a(5,3)

运行效果如下:

图片描述

lambda 函数设置参数默认值

a = lambda x=5:x**2
a()
a(3)

运行效果如下:

图片描述

lambda 函数配合括号直接使用

(lambda x:x**2)(5)

运行效果如下:

图片描述


lambda 函数的注意事项

有可能你会想,既然匿名函数这么简洁,我们只用匿名函数岂不美哉?

当然不能,匿名函数和命名函数都有自己的应用场景,匿名函数大多数时候只能作为一种补充,一种小技巧,也就是通常说的“语法糖”。

糖的价值是显而易见的,能够提供能量,能够让人心情愉悦,但糖吃太多了就会对身体产生影响,轻则牙疼,重则糖尿病、重度肥胖等多种疾病。

所以你应该知道,语法糖可以让我们书写代码更加轻松,更加愉悦,但一定要适度。在简洁明了的前提下,我们可以使用 lambda 函数,但不能为了使用而使用,破坏了代码的可读性

以下是 lambda 函数的一些使用建议:

  • 如果只是一次性使用,我们可以使用匿名函数,该语法可以节省内存空间。
  • 匿名函数只能有一行表达式,参数是可选的,但必须有返回值。
  • 匿名函数应避免太过复杂,影响可读性。
  • 匿名函数的目的是简洁,当匿名函数涉及复杂结构时,应当改用命名函数。

5.8 文档字符串

文档字符串是一个非常重要的工具,一般主要用于解释程序的使用,从而让程序更加简单易懂。

我们一般在函数体的第一行使用三引号('''...''' 或 """...""") 来定义文档字符串。

文档字符串

文档字符串其实就是一种帮助文档,可以包含函数的基本信息、功能介绍、参数的类型、函数接口的使用,以及其他注意事项。


接下来,我们首先通过查看内置函数的文档是如何写的,我们可以使用内置的 help() 函数查看。

比如,我们首先查看一下 print() 函数的帮助文档,只需把函数名作为参数传入 help() 函数内即可。

help(print)

运行效果如下:

图片描述

q 退出。

也许你会觉得 help() 函数这样展示无法对照着学习,那还可以使用另外一个方式。


可以使用特殊方法 __doc__(注意双下划线)调用函数中的文档字符串。

我们继续以查看 print 函数为例。

print(print.__doc__)

运行效果如下:

图片描述


再举一个例子,查看 str() 函数的帮助文档。

print(str.__doc__)

运行效果如下:

图片描述

通过上面两个例子,我们大致看到,函数的帮助文档首先是函数的使用方式(其实包括参数的使用),然后是一个空行分隔,最后是具体描述函数功能和使用。

接下来,我们也尝试写一个函数的文档字符串。


文档字符串的使用

前面我们通过分析内置函数的文档字符串的书写,总结出函数的帮助文档首先是函数的使用方式(其实包括参数的使用),然后是一个空行分隔,最后是具体描述函数功能和使用。

接下来我们给下面函数增加文档字符串,然后在 Python 交互环境运行。

def hello(name,greet="Hello"):
    print(f'{greet} {name}!')

按照上面添加文档字符串的一般原则,我们首先应该写函数的使用方式,然后是一个空行,最后是一些描述。

对照我们函数的内容,增加文档字符串后的内容如下,大家也可以组织自己的语言来书写。

def hello(name,greet="Hello"):
    '''\
hello(name)
hello(name,greet="Hello")

这是一个问好的程序,参数 name 表示问好的对象,
可选参数 greet 是问好的方式,默认是 Hello。'''
    print(f'{greet} {name}!')

我们把函数写入 Python 交互模式,注意缩进统一使用四个空格。

然后我们就可以使用特殊方法 __doc__ 查看函数的帮助信息了。

print(hello.__doc__)

运行效果如下:

图片描述

也可以通过 help() 函数查看

help(hello)

运行效果如下:

图片描述

上面只是一种一般意义上的文档字符串,通过阅读别人的代码,我们还会学到更多更好的文档字符串书写规范。


总结

我们学习了 Python 函数文档字符串的书写,前面我们多次说过,代码写出来不止是给机器使用,很多时候还是给人看的,要想让人更好的阅读你的代码,就要养成写注释的习惯,文档字符串就是函数的注释。


函数章节总结

  1. 函数是指一段可以直接被另一段程序或代码引用的程序或代码。

  2. 函数接收参数,返回结果,这个结果被称为函数的返回值。有的函数没有返回值,只有输出值,比如 print() 函数。

  3. 函数的调用使用函数名加函数调用运算符括号()

  4. 函数的参数分为 形参实参,形参就是形式上的参数,实参就是实际的参数,形参只起到占位置的作用。

  5. 位置参数也叫必选参数,在调用函数时对应位置必须有该参数,否则会报错。

  6. 默认值参数是指在定义函数时给参数指定默认值,这样,在调用函数时,如果我们对默认参数没有赋值,则该参数保持为默认值;如赋值则改变为新值。默认参数后面不能有非默认参数

  7. 关键字参数是指在调用函数时直接对形参赋值,使用关键字参数的好处是不用在乎参数传入的顺序。

  8. return 关键字是给函数设置返回值的,方便其他地方调用,不设置 return 或者 return 内容为空都代表返回 None。不设置 return 只是没有返回值,但不会影响函数的输出。print() 函数就没有返回值。

  9. Python 中有两种基本的变量作用域:局部变量全局变量。在函数内部定义的变量名只能被函数内部引用,不能在函数外引用,这个变量的作用域就是局部的,所以它就是一个局部变量。函数内部只作引用的 Python 变量会被隐式的视为全局变量。

  10. 如果在函数内部任何位置为变量赋值,除非明确声明为全局变量,否则均将其视为局部变量。修改全局的不可变类型才需要声明,像列表、字典这种可变数据类型,使用内置方法修改元素时,其对象并没有发生变化,不需要声明。

  11. 匿名函数是一种没有函数名的函数,使用匿名函数的可以减少代码量、使代码结构更加紧凑。我们使用 lambda 关键字创建匿名函数,因此也称 lambda 函数或 lambda 表达式。

  12. 文档字符串是一个重要工具,用于解释文档程序,帮助程序更加简单易懂。