函数是一段组织好的,可重复使用的,用来实现特定功能的代码块。
为了更好地理解函数,我们可以模拟用榨汁机做一杯美味的猕猴桃汁:
print('将猕猴桃去皮')
print('将猕猴桃切块')
print('将切碎的猕猴桃放进榨汁机')
print('将果汁倒进杯子中')
print('制作完成!')
如果我们这样模拟此过程的话,无疑非常的麻烦,我们后续只要想再做一杯猕猴桃汁我们又要打五行代码。这里我们可以充分的发挥的函数的作用,将我们制作猕猴汁的过程封装进一个函数中。你也可以简单的理解为我们把某件事情的步骤教给 Python,并用一句“暗号”来指代要让 Python 做的事情,以后只要对 Python 说出这个“暗号”,Python 就能把整个事情做完。
def make_juice():
print('将猕猴桃去皮')
print('将猕猴桃切块')
print('将切碎的猕猴桃放进榨汁机')
print('将果汁倒进杯子中')
print('制作完成!')
上面的代码,第一行使用了 def make_juice(): 这样的格式,def 是 define(中文含义:定义)的缩写,表示你要向 Python 传授做事流程了,make_juice 是你定义的流程暗号名。在这一行的末尾,一定要加上一对 英文括号 和一个 英文冒号 。
从第二行开始,我们要开始写制作猕猴桃汁的代码了。我们发现,它们的前面统一开头空了两格,也就是我们在第三关说到的 缩进,这是用来表示:这几行代码属于内部确定的做事流程。
这就是完整的教 Python 做事的代码结构,要注意以下几点:
这样,Python 就记住了, make_juice 是它学会的“新暗号”,专门用来制作猕猴桃汁。以后,我们只要使用 暗号名() 的格式,就能让 Python 进行我们刚刚教会它的一系列操作
注意:Python 的阅读顺序和我们人类是一样的,都是从上向下,一行一行地读。所以我们应该注意定义函数一定要在使用函数之前。
前面教 Python 做事,并设定好“暗号”的过程,叫作 定义函数。其中缩进的几行表示具体处理过程的代码,叫 函数体。而后面让 Python 真正根据暗号做事的过程,叫作 调用函数,我们可以让 Python 执行无数次暗号,也就是说,函数一旦定义,就可以任我们随意调用。
有了 参数,函数就更加灵活而强大了。定义函数时,我们只要专心总结出固定的操作流程,至于具体根据场景而变化的元素,只要交给 参数 去统一表示就可以。
函数的参数是可有可无的。并不是一个函数的必需元素。如果有多个参数则要用逗号隔开
根据不同的需求,Python 中的参数按传入方式分为两种:
例如我现在构造一个record 的函数,并将参数传入顺序定为:
def record(name, age, height, weight):
print('欢迎小朋友们!')
print('姓名:', name)
print('年龄:', age)
print('身高:', height, 'cm')
print('体重:', weight, 'kg')
# 下面开始调用定义的函数,传入关键信息。
record('野原新之助', 5, 110, 22)
record('风间彻', 5, 112, 21)
# 输出:
# 欢迎小朋友们!
# 姓名: 野原新之助
# 年龄: 5
# 身高: 110 cm
# 体重: 22 kg
# 欢迎小朋友们!
# 姓名: 风间彻
# 年龄: 5
# 身高: 112 cm
# 体重: 21 kg
此时我们调用函数的时候就用到了 位置参数,按位置,依次传入了 name、age、height、weight 四个参数纪录小新和风间的信息。
还是上面的场景我们可以修改一下代码:
def record(name, age, height, weight):
print('欢迎小朋友们!')
print('姓名:', name)
print('年龄:', age)
print('身高:', height, 'cm')
print('体重:', weight, 'kg')
record(age=5, weight=21, name='佐藤正男', height=109)
# 输出:
# 欢迎小朋友们!
# 姓名: 佐藤正男
# 年龄: 5
# 身高: 109 cm
# 体重: 21 kg
上面的代码中,在调用函数时按照 参数名 = 值 的格式传入了参数,这种参数就叫作 关键字参数。
在位置参数中,我们需要记得定义函数时每个参数对应的位置,而关键字参数可以 无视位置。我们可以很轻松地给对应的参数赋值,并且能一眼看出各个参数所对应的含义,在以后检查我们的代码时,也就更加方便了。
位置参数可以和关键字参数一起使用,但要保证所有位置参数都在关键字参数前,否则程序会因为识别错乱而报错。
为了减少重复输入,我们引入了默认参数
默认参数一定要放在非默认参数的后面,否则程序会出现错误
例如:
# 我们可以在定义函数时,设置年龄默认为 5
def record(name, height, weight, age=5):
print('欢迎小朋友们!')
print('姓名:', name)
print('年龄:', age)
print('身高:', height, 'cm')
print('体重:', weight, 'kg')
# 调用函数时,遇到 5 岁的小朋友,可以不输入年龄
record('阿呆', 115, 25)
# 输出:
# 欢迎小朋友们!
# 姓名: 阿呆
# 年龄 5
# 身高 115 cm
# 体重 25 kg
# 输入妮妮的信息时,要在最后填上她的年龄
record('樱田妮妮', 105, 18, 4)
# 输出:
# 欢迎小朋友们!
# 姓名: 樱田妮妮
# 年龄: 4
# 身高: 105 cm
# 体重: 18 kg
问题 1:在函数内部,针对参数使用 赋值语句,会不会影响调用函数时传递的 实参变量? —— 不会!
def demo(num, num_list):
print("函数内部")
# 赋值语句
num = 200
num_list = [1, 2, 3]
print(num)
print(num_list)
print("函数代码完成")
gl_num = 99
gl_list = [4, 5, 6]
demo(gl_num, gl_list)
print(gl_num)
print(gl_list)
问题 2:如果传递的参数是 可变类型,在函数内部,使用 方法 修改了数据的内容,同样会影响到外部的数据
def mutable(num_list):
# num_list = [1, 2, 3]
num_list.extend([1, 2, 3])
print(num_list)
gl_list = [6, 7, 8]
mutable(gl_list)
print(gl_list)
+=
python
中,列表变量调用 +=
本质上是在执行列表变量的 extend
方法,不会修改变量的引用def demo(num, num_list):
print("函数内部代码")
# num = num + num
num += num
# num_list.extend(num_list) 由于是调用方法,所以不会修改变量的引用
# 函数执行结束后,外部数据同样会发生变化
num_list += num_list
print(num)
print(num_list)
print("函数代码完成")
gl_num = 9
gl_list = [1, 2, 3]
demo(gl_num, gl_list)
print(gl_num)
print(gl_list)
有时可能需要 一个函数 能够处理的参数 个数 是不确定的,这个时候,就可以使用 多值参数
python
中有 两种 多值参数:
*
可以接收 元组*
可以接收 字典一般在给多值参数命名时,习惯使用以下两个名字
*args
—— 存放 元组 参数,前面有一个 *
**kwargs
—— 存放 字典 参数,前面有两个 *
args
是 arguments
的缩写,有变量的含义
kw
是 keyword
的缩写,kwargs
可以记忆 键值对参数
def demo(num, *args, **kwargs):
print(num)
print(args)
print(kwargs)
demo(1, 2, 3, 4, 5, name="小明", age=18, gender=True)
提示:多值参数 的应用会经常出现在网络上一些大牛开发的框架中,知道多值参数,有利于我们能够读懂大牛的代码
需求
sum_numbers
,可以接收的 任意多个整数def sum_numbers(*args):
num = 0
# 遍历 args 元组顺序求和
for n in args:
num += n
return num
print(sum_numbers(1, 2, 3))
args
kwargs
*
*
def demo(*args, **kwargs):
print(args)
print(kwargs)
# 需要将一个元组变量/字典变量传递给函数对应的参数
gl_nums = (1, 2, 3)
gl_xiaoming = {"name": "小明", "age": 18}
# 会把 num_tuple 和 xiaoming 作为元组传递个 args
# demo(gl_nums, gl_xiaoming)
demo(*gl_nums, **gl_xiaoming)
return语句
函数体中, return c 的意思是,将 c 指定为唯一可以和外界交流的数据,外面的代码想要获取内部运行结果,就丢个 c 的值给他。至于外面如何处置,函数就管不着了。我们可以理解为return的作用就是函数内部与外部世界的“信使”,负责将函数内部的值传递给外面。
被 return 语句传到函数外的值,叫作函数的 返回值。返回值 要么被外部代码直接打印,要么赋值给变量,要么参与运算等。
return 语句有一个特别要注意的点,return 语句是函数执行结束的标记,运行完 return 语句,就会退出函数的执行,不管后面还有没有其它代码。
和函数的参数一样,函数也支持 0 到多个返回值。
多个返回值也和多参数一样,放在关键词 return 后面并列,然后用英文逗号隔开。
例如:
def new_kids(kid1, kid2, kid3, kid4):
# 多个值在 return 后并列,用英文逗号间隔开
return kid1, kid2, kid3, kid4
print(new_kids('野原新之助', '风间彻', '佐藤正男', '樱田妮妮'))
# 输出:('野原新之助', '风间彻', '佐藤正男', '樱田妮妮')
我们将多个小朋友的姓名在 return 后并列了出来,最后函数的返回值是 (‘野原新之助’, ‘风间彻’, ‘佐藤正男’, ‘樱田妮妮’)。
由此可知函数若有多个返回值则会以元组的方式返回!
函数的命名和变量的命名是一样的,由数字、字母、下划线组成,并且不可以用数字作为名称的开头。另外,名称本身要表达自己的功能,要简单易读,并且中间不能有空格。如果有多个单词,要用下划线 _ 连接。
在 Python 中变量分为两种:
全局变量 在当前写的代码中一直起作用。全局变量 有效的范围,叫作 全局作用域。
局部变量 只在一定范围内有效,在范围外就无效了。
在函数中定义的变量都是 局部变量 。局部变量 有效的范围,叫作 局部作用域。要和外界交流,除非将某个变量用 return 语句变成函数的返回值。
由于 全局变量 作用于整个环境,所以就算跑到自成一体的函数内部,也是有效的。而函数内部的 局部变量 出了函数外,离开自己的 局部作用域,就无效了。
下面,我们再来思考一个问题,全局变量名和局部变量名可以相同吗?
答案是可以。如果局部变量和全局变量名称相同,那函数内部会优先访问局部变量,外部只会访问全局变量(外部是不会去访问局部变量的,当然他也访问不到)。有了函数这堵“围墙”的保护,两种变量被完美地隔开,互不干扰,在各自的作用域内生效。
我们可以在函数内部的局部变量前加上global关键字,我们就可以将局部变量转化为全局变量。加了 global 关键字后,相当于我们宣布,函数里的东西大家可以一起用。上面例子中,class_name 就成了全局变量,函数外也能使用,便不会再报错了。
使用global的情景:
test2
中,调用了另外一个函数 test1
test1
函数时,会先把函数 test1
中的任务都执行完test2
中调用函数 test1
的位置,继续执行后续的代码def test1():
print("*" * 50)
print("test 1")
print("*" * 50)
def test2():
print("-" * 50)
print("test 2")
test1()
print("-" * 50)
test2()
体会一下工作中 需求是多变 的
需求 1
print_line
函数能够打印 *
组成的 一条分隔线def print_line(char):
print("*" * 50)
需求 2
def print_line(char):
print(char * 50)
需求 3
def print_line(char, times):
print(char * times)
需求 4
提示:工作中针对需求的变化,应该冷静思考,不要轻易修改之前已经完成的,能够正常执行的函数!
def print_line(char, times):
print(char * times)
def print_lines(char, times):
row = 0
while row < 5:
print_line(char, times)
row += 1
# 缺少英文括号后的英文冒号
def func1()
print('Hello World')
# 报错:SyntaxError: bad input on line 1
# (语法错误:第 1 行输入有问题)
yntaxError 的意思是“语法错误”,而冒号后面的是语法错误的细节。这个例子中的具体细节为 bad input,一般来说就是你输入的代码不符合格式,可能多打或少打了一些必要的字母或者标点。最后的 on line xxx 意思是在哪一行发现的错误,这也有助于你直接去定位。
# 输入了中文标点(本例中输入了中文冒号)
def func2():
print('Hello World')
# 报错:SyntaxError: invalid character in identifier
# (语法错误:识别不了不支持的字符)
这里的语法错误细节是 invalid character,也就是 Python 语法中不支持的字符。除字符串和注释外,Python 语法里支持的字符是不支持汉字和中文标点的。
这里的例子中输入了中文的冒号,Python 当然识别不了啦。所以一定要注意,见到 invalid character 就要看看是不是把括号、冒号、引号等输入错啦,平时写代码时,我们也要预先把输入法调好。
# y = 2 缩进错误
def func3():
x = 1
y = 2
# 报错:IndentationError: unindent does not match any outer indentation level
# (缩进错误:(y = 2 这一行)缩进变小了,但是其它地方匹配不到和他一样的缩进层级)
IndentationError 指“缩进错误”。“indentation” 和“indent” 表示缩进,“unindent” 表示“减少或取消缩进”。我们在第五关中说过,Python 非常注重缩进的层次,一旦确定了缩进层次,后面就要统一。一开始用两个或者四个空格作为一个缩进,后面也需要一直保持一样的标准,否则机器就会识别错乱。
比如这个例子中,第 2 行 x = 1 用两个空格表示一个缩进,但第 3 行 y = 2 中变成了一个空格缩进,发生了报错语句所说的“unindent”,但在其它代码行中,找不到同样是 1 个空格表示缩进的地方,所以报错了。
# 默认参数应该放在位置参数后
def func4(x, y = 1, z):
print(x, y, z)
# 报错:SyntaxError: non-default argument follows default argument
# (语法错误:非默认参数被放到了默认参数后面)
这里的语法错误细节中的关键词是“argument”,意思是“参数”。“default argument” 表示“默认参数”。当你看到“argument”,就要去检查函数定义过程中,参数有没有出错啦。
# 全局作用域不能访问局部变量
def func5():
x = 1
print(x)
# 报错:NameError: name 'x' is not defined
# (变量名错误:变量名 'x' 没有被定义)
局部变量被函数这堵“围墙”隔得严严实实。在函数外,不用 global 语句,是无法访问函数内的局部变量的。