在编程中,我们经常要调用相同或者类似的操作,这些相同或者类似的操作是由同一段代码完成的,而函数的出现,可以帮助我们避免重复编写这些代码。函数的作用就是把相对独立的某个功能抽象出来,使之成为一个独立的实体。
例如,我们开发一个支持人与人之间对话的社交网站,“对话”这个功能实现起来比较复杂,我们可以将它封装为一个函数,每次调用函数就可以发起对话。大型网站都有日志功能,所有重要操作都会记录日志,而日志处理需要由多行Python文件操作的相关代码组成,将这些代码组装为函数,每次写日志调用此函数即可。
1. 函数的定义
定义一个函数以 def 开头
def function_name(arg1, arg2):
function body
return value
函数名(function_name): 和Python中其他标识符命名规则相同,有效的函数名以字母或下划线开头,后面可以跟字母、数字或下划线,函数名应该能反映函数的功能。
注意: Python 中函数名区分大小写,字母相同但是大小写不同的函数视为两个不同的函数
函数参数(arg1, arg2): 调用一个函数时可以传递的参数,参数可以有一个或多个,也可以没有参数
函数内容(function body): 任何有效的代码都可以出现在函数内部。函数内容和 def 缩进 4 个空格
函数返回值(return value): 函数执行完成后返回的值。也可以不返回任何内容,不返回内容可视为返回 “None”
def introduce(name):
print("Hello", name)
introduce("world") # Hello world
introduce("soulballad") # Hello soulballad
2. 函数的参数
在创建函数时,可以设置参数,也可以不设置参数。对于设置参数的函数,调用时需要向函数内传递参数,被传入的参数称为实参,函数定义时的参数为形参。
Python 中的参数可以分为以下几种类型:
- ❤ 1. 必须参数
- ❤ 2. 关键字参数
- ❤ 3. 默认参数
- ❤ 4. 可变参数
- ❤ 5. 组合参数
2.1 必须参数
必须参数:顾名思义就是函数调用时必须传入,并且在调用时数量和顺序必须和定义时参数保持一致
def add(a, b):
print("a + b =", a + b)
add(1, 2) # a + b = 3
add(1) # 如果只传入一个参数,出现错误: TypeError: add() missing 1 required positional argument: 'b'
add(1,2,3) # 如果多传入一个参数,出现错误: TypeError: add() takes 2 positional arguments but 3 were given
2.2 关键字参数
使用关键字参数可以不按函数定义时的参数顺序来调用函数,Python解释器能够根据函数定义时的参数名字来匹配参数
def hello(name, age):
print("姓名:", name)
print("年龄:", age)
# 按顺序传递参数
hello(name="零一", age=18)
# 姓名: 零一
# 年龄: 18
# 不按顺序传递参数
hello(age=3, name="小明")
# 姓名: 小明
# 年龄: 3
hello(name="张三",age=20,gender="男") # 传入没有定义的参数,出现错误: TypeError: hello() got an unexpected keyword argument 'gender'
2.3 默认参数
在定义函数时可以给函数添加默认值,如果调用函数时没有传入参数,函数就会使用默认值,并且不会像必传参数那样报错
def default_value(name, age=18):
print("我的名字是:", name)
print("我今年:", age, "岁")
default_value("零一")
# 我的名字是: 零一
# 我今年: 18 岁
注意: 默认参数必须定义在最后,而且在默认参数之后定义必须参数会报错
def default_value(age=10, name): print("我的名字是:", name) print("我今年:", age, "岁") # SyntaxError: non-default argument follows default argument
默认参数用法:
def student_score(name, score=60, location="Shanghai"):
print("姓名:", name)
print("成绩:", score)
print("地区:", location)
print("---------- 传入所有参数 ----------")
student_score("张三", 100, "Beijing")
print("---------- 不传最后一个参数 ----------")
student_score("小明", 80)
print("---------- 不传中间参数 ----------")
student_score("小红", location="广州")
print("---------- 只传必须参数 ----------")
student_score("胖虎")
print("---------- 只传关键字参数 ----------")
student_score(name="元太")
# ---------- 传入所有参数 ----------
# 姓名: 张三
# 成绩: 100
# 地区: Beijing
# ---------- 不传最后一个参数 ----------
# 姓名: 小明
# 成绩: 80
# 地区: Shanghai
# ---------- 不传中间参数 ----------
# 姓名: 小红
# 成绩: 60
# 地区: 广州
# ---------- 只传必须参数 ----------
# 姓名: 胖虎
# 成绩: 60
# 地区: Shanghai
# ---------- 只传关键字参数 ----------
# 姓名: 元太
# 成绩: 60
# 地区: Shanghai
2.4 可变参数
在某些情况下,不能再定义时就确定参数的数量和内容,这时就可以使用可变参数
2.4.1 语法
可变参数语法如下:
some_func(*args, **kwargs)
- some_func 为函数名
- *args 和 **kwargs 为可变参数
2.4.2 *args可变参数
def foo(*args):
print(args)
foo()
foo(1, 2)
foo("Python", "function", "parameters")
# ()
# (1, 2)
# ('Python', 'function', 'parameters')
2.4.3 **kwargs可变参数
def foo(**kwargs):
print(kwargs)
foo()
foo(name="python study")
# {}
# {'name': 'python study'}
2.4.4 混合参数
从上面例子可以看出: *args 参数获取到的是一个元组, **kwargs 参数获取到的是一个字典。在日常使用中,*args 和 **kwargs 经常出现,用于解决一些未知问题
def calculate_sum(*args, **kwargs):
s = 0
for i in args:
s += i
print("输入的数字之和是:", s)
for k, v in kwargs.items():
print(k, v)
calculate_sum(1, 2, 3, 4, 5, name="python")
# 输入的数字之和是: 15
# name python
2.4.5 可变参数传递未知参数
使用可变参数的方式来传递未知参数
def exp(*args, **kwargs):
print(args)
print(kwargs)
l = (1, 2, 3, 4)
d = {"参数1": "arg1", "参数2": "arg2"}
exp(l, d)
exp(*l, **d)
# ((1, 2, 3, 4), {'参数1': 'arg1', '参数2': 'arg2'})
# {}
# (1, 2, 3, 4)
# {'参数1': 'arg1', '参数2': 'arg2'}
3. 变量作用域
Python 中有两种最基本的变量作用域:局部变量和全局变量
3.1 局部变量
一般情况下,在函数内赋值的变量,不做特殊声明的变量都是局部变量
def foo():
x = "hello"
print(x)
foo() # hello
3.2 全局变量
在函数外赋值的变量就是全局变量,全局变量可以在整个程序范围内被访问
3.2.1 全局变量使用
x = "hello"
def foo():
print(x)
foo() # hello
print(x) # hello
3.2.2 重写全局变量
函数体内重写赋值的同名变量,不会改变函数体外的全局变量
x = "函数体外"
def foo():
x = "函数体内"
print(x)
foo() # 函数体内
print(x) # 函数体外
3.2.3 修改全局变量
使用 global 关键字在函数体内对函数体外的全局变量进行修改
x = "函数体外"
def foo():
global x
x = "函数体内"
print(x)
foo() # 函数体内
print(x) # 函数体内
4. 函数返回值
如果想要获取函数中的局部变量,可以使用 “return” 关键字进行返回
4.1 有返回值
def foo():
x = "局部变量"
return x
result = foo()
print(result) # 局部变量
4.2 无返回值
def no_return():
print("没有return")
def no_return_value():
print("有return没有返回值")
return
def has_return():
x = "局部变量"
print("有return有返回值")
return x
result1 = no_return()
print(result1)
# 没有return
# None
result2 = no_return_value()
print(result2)
# 有return没有返回值
# None
result3 = has_return()
print(result3)
# 有return有返回值
# 局部变量
4.3 多个返回值
def multi_value():
r1 = "第一个返回值"
r2 = "第二个返回值"
r3 = "第三个返回值"
r4 = "第四个返回值"
r5 = "第五个返回值"
return r1, r2, r3, r4, r5
s = multi_value()
print(s) # ('第一个返回值', '第二个返回值', '第三个返回值', '第四个返回值', '第五个返回值')
从执行结果来看,有多个返回结果时,Python 会返回一个元组;当Python返回了元祖时,就可以赋值给多个变量了
4.4 获取多个返回值
return "第一个返回值", "第二个返回值"
r1, r2 = two_value()
print(r1) # 第一个返回值
print(r2) # 第二个返回值
5. Lambda表达式
Lambda 表达式也称作匿名函数。
5.1 Lambda 定义
以 “lambda” 开头,就表示是 lambda 表达式。它由 “:” 分为两部分,左边的是函数的参数,右边的是要返回的值。
lambda 表达式不需要用 return 关键字返回内容,函数默认会返回 “:” 右边的值
def add(x, y):
return x+y
lambda x,y: x+y
5.2 Lambda 使用场景
lambda 表达式一般有两种使用情况:
- 程序只执行一次,不需要定义函数,使用 lambda 表达式方便定义,且节省了内存中变量的定义
- 在某些函数中必须以函数作为参数,但是函数本身十分简单且在一处使用
f = lambda x, y: x+y
z = f(1,2)
print(f) # <function <lambda> at 0x000001D1DC518B80>
print(z) # 3
5.3 filter过滤
filter 是 Python的内置函数,用于过滤序列,过滤掉不符合条件的元素。
filter 函数的第一个参赛需要传入另一个函数,传入的函数用来作为筛选条件,满足条件的返回 “True”,否则返回 “False”。
l1 = [1,2,3,4,5,6,7,8]
l2 = [item for item in filter(lambda x: x>5, l1)]
print(l2) # [6, 7, 8]
6. 扩展知识
6.1 文档字符串
使用 def 定义的函数,第一行可以是字符串,这个字符串就是文档字符串
def add(x, y):
"""
返回参数x和y的两数之和
:param x: int 第一个参数
:param y: int 第二个参数
:return: 返回 x+y
"""
return x + y
print(add(1, 2)) # 3
# 可以使用 __doc__ 方式获取文档字符串
print(add.__doc__)
# 返回参数x和y的两数之和
# :param x: int 第一个参数
# :param y: int 第二个参数
# :return: 返回 x+y
6.2 内置函数
Python 解释器内置了很多不同功能和类型的函数,可以直接使用
截取自菜鸟教程:Python 内置函数
6.3 函数注释
函数注释是一个可选功能,它允许在函数参数和返回值中添加任意的元数据
函数注释定义如下:
def function_name(a:expression, b:expression) -> expression:
function body
return value
def compile(source: "something compilable",
filename: "where the compilable thing comes from",
mode: "is this a single statement or a suite?") -> bool:
return True
print(compile.__annotations__)
# {'source': 'something compilable', 'filename': 'where the compilable thing comes from', 'mode': 'is this a single statement or a suite?', 'return': <class 'bool'>}