学Python数据科学,玩游戏、学日语、搞编程一条龙。
整套学习自学教程中应用的数据都是《三國志》、《真·三國無雙》系列游戏中的内容。
在所有编程和脚本语言中,函数是可以在程序中重复使用的程序语句块。它节省了开发人员的时间。在 Python 中函数的概念与其他语言中的相同。有一些内置函数是 Python 的一部分。除此之外,我们可以根据需要定义函数。
函数的数学概念是一个或多个输入与一组输出之间的关系或映射。
函数是封装特定任务或相关任务组的自包含代码块。
过去的内容里已经了解到的 Python 内置函数。
# 唯一整数标识符
s = '曹操孫権'
id(s)
56313440
# 参数的长度
a = ['曹操', '孫権', '劉備', '袁紹']
len(a)
4
对于函数来说不需要知道代码在哪里工作,甚至不需要知道代码是如何工作的。只需要知道函数的接口,需要什么参数(如果有的话)以及返回什么值(如果有的话)。
Python 函数的重要性
抽象和可重用性
假设编写了一些非常重要或者使用率非常频繁的代码的代码,随着后续的开发会发现该代码执行的任务经常应用于不同的位置。如果想偷懒的话肯定不能在编辑器里不断的使用 CV(Ctrl+c,Ctrl+v) 大法吧。
对于已经完成的代码会在后续版本改进的时候也可能会发现有些地方需要调整、修复或者增加其他的功能,那么就需要在之前相同复制的地方进行修改,这样会很麻烦并且也非常容易出错。
最好的解决方案是定义一个执行任务的 Python 函数。在应用程序中每次只需调用该函数即可。而改变函数执行的代码内容值需要更改一个位置就可以将其他执行相同函数的地方一同修改。
将功能抽象为函数定义是软件开发中不重复(DRY)原则。
模块化
函数允许将复杂的过程分解成更小的步骤(单独的函数),每个函数执行自定义的任务,在主程序中需要使用的时候进行调用即可。
程序读写处理文件的例子。
def read_file():
# 读取文件操作
<statement>
read_file()
def process_file():
# 处理文件操作
<statement>
process_file()
def write_file():
# 写入文件操作
<statement>
write_file()
命名空间
命名空间是程序中标识符。调用 Python 函数时会为该函数创建一个新的命名空间,该命名空间不同于所有其他已经存在的命名空间。函数中编写代码时可以使用变量名和标识符,而不必担心它们是否已在函数之外的其他地方使用。这有助于大大减少代码中的错误。
函数调用和定义
定义 Python 函数语法。
"""
def 通知 Python 正在定义函数的关键字
<function_name> 命名函数的有效 Python 标识符
<parameters> 可以传递给函数的可选参数列表,以逗号分隔
: 表示 Python 函数头(名称和参数列表)结尾的标点符号
<statement(s)> 一组有效的 Python 语句
"""
def <function_name>([<parameters>]):
<statement(s)>
调用 Python 函数语法。
"""
<function_name> 命名函数的有效 Python 标识符
<arguments> 可以传递给函数的可选参数列表,以逗号分隔
"""
<function_name>([<arguments>])
调用函数 f() 举例。
def f():
s = '这是一个函数 f'
print(s)
print('调用函数 f 前')
f()
print('调用函数 f 后')
调用函数 f 前
这是一个函数 f
调用函数 f 后
调用空函数举例。
def f():
pass
f()
参数传递
可以将数据传递到函数中,以便其行为在一次调用中会有所不同。
位置参数,在函数定义中括号内指定逗号分隔的参数列表。
def f(name, vesion, price):
print(f'{
name} {
vesion} {
price:.2f}')
调用函数时需要指定相应的参数列表,调用中参数的顺序必须与定义中参数的顺序相匹配。
f('三國志', 'V', 199.00)
三國志 V 199.00
函数定义中给出的参数称为形参,函数调用中的实参称为实参。
调用和定义的参数循序和数量都要保持一致否则会出现错误。
f('三國志', 'V')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required positional argument: 'price'
f('三國志', 'V', 199.00 , 1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() takes 3 positional arguments but 4 were given
关键字参数
当调用一个函数时可以在表单中指定参数 <keyword>=<value>。在这种情况下,每个都 <keyword> 必须匹配 Python 函数定义中的参数。
f(name='三國志', vesion='V', price=199.00)
三國志 V 199.00
引用与任何声明的参数都不匹配的关键字会生成异常。
f(name='三國志', vesion='V', price_=199.00)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() got an unexpected keyword argument 'price_'
使用关键字参数顺序无限制。
f(vesion='V', name='三國志',price_=199.00)
三國志 V 199.00
参数和参数的数量仍必须匹配。
f(name='三國志', vesion='V')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: f() missing 1 required positional argument: 'price'
位置参数和关键字参数混用,但是指定一个关键字参数,右边就不能有任何位置参数。
f('三國志', 'V', price_=199.00)
三國志 V 199.00
f('三國志', vesion='V', 199.00)
SyntaxError: positional argument follows keyword argument
默认参数
默认或可选参数,函数定义中指定的参数具有格式 <name>=<value>,则 <value> 成为该参数的默认值。
"""
位置参数的顺序和数量必须与函数定义中声明的参数一致。
关键字参数的数量必须与声明的参数一致,但它们可以以任意顺序指定。
默认参数允许在调用函数时省略一些参数。
"""
def f(name, vesion, price=199.00):
print(f'{
name} {
vesion} {
price:.2f}')
f('三國志', 'I', price=199.00)
三國志 I 199.00
f('三國志', 'I')
三國志 I 199.00
可变的默认参数值
def f(my_list=[]):
my_list.append('###')
return my_list
# 携带参数传递
f(['曹操', '孫権', '劉備'])
['曹操', '孫権', '劉備', '###']
# 无参数传递
f()
['###']
f()
['###', '###']
Python 中的按值传递与按引用传递
Python 中的参数传递在某种程度上是按值传递和按引用传递的混合体。传递给函数的是对对象的引用,但引用是按值传递的。
使用 id() 观察将参数传递给函数时,会发生重新绑定。
def f(fx):
print('fx =', fx, '/ id(fx) = ', id(fx))
fx = 10
print('fx =', fx, '/ id(fx) = ', id(fx))
x = 5
print('x =', x, '/ id(x) = ', id(x))
x = 5 / id(x) = 1484418240
# f()首次启动时,两者fx都x指向同一个对象,而fx被重新定义变量指向另一个对象
f(x)
fx = 5 / id(fx) = 1484418240
fx = 10 / id(fx) = 1484418400
print('x =', x, '/ id(x) = ', id(x))
x = 5 / id(x) = 1484418240
函数不会通过参数传递重新分配参数的值的情况。
# 参数传递到不同的数据类型中
def f(x):
x = '曹操'
for i in (
40,
dict(曹操=1, 孫権=2),
{
1, 2, 3},
'孫権',
['曹操', '孫権', '劉備']):
f(i)
print(i)
40
{
'曹操': 1, '孫権': 2}
{
1, 2, 3}
孫権
['曹操', '孫権', '劉備']
函数调用修改数据中的值的情况。
# 列表通过函数更新数据
def f(x):
x[0] = '---'
my_list = ['曹操', '孫権', '劉備', '袁紹']
f(my_list)
my_list
['---', '孫権', '劉備', '袁紹']
# 字典通过函数更新数据
def f(x):
x['孫権'] = 22
my_dict = {
'曹操': 1, '孫権': 2, '劉備': 3}
f(my_dict)
my_dict
{
'曹操': 1, '孫権': 22, '劉備': 3}
参数传递,将不可变对象(如 int, str ,tuple 或 frozenset)传递给 Python 函数的行为类似于按值传递,该函数不能在调用环境中修改对象。
return 声明
Python 函数中的 return 语句有两个目地:退出函数 和 返回调用。
退出函数,return 语句会导致 Python 函数立即退出并将执行转移回调用者。
def f():
print('曹操')
print('孫権')
return
f()
曹操
孫権
return 语句不需要位于函数的末尾,可以出现在函数体的任何位置,甚至可以出现多次。
def f(x):
if x < 0:
return
if x > 100:
return
print(x)
f(-1)
f(110)
f(33)
33
返回调用,return 语句还用于将数据传回给调用者。
# 调用
def f():
return '曹操'
s = f()
print(s)
'曹操'
# 字典操作
def f():
return dict(曹操=1, 孫権=2, 劉備=3)
print(f())
{
'曹操': 1, '孫権': 2, '劉備': 3}
print(f()['劉備'])
3
# 字符串切片操作
def f():
return '曹操孫権'
print(f()[2:4])
'孫権'
# 列表操作
def f():
return ['曹操', '孫権', '劉備', '袁紹']
print(f())
['曹操', '孫権', '劉備', '袁紹']
print(f()[2])
'劉備'
# 元组操作
def f():
return '曹操', '孫権', '劉備', '袁紹'
print(type(f()))
<class 'tuple'>
t = f()
print(t)
('曹操', '孫権', '劉備', '袁紹')
a, b, c, d = f()
print(f'a = {
a}, b = {
b}, c = {
c}, d = {
d}')
a = 曹操, b = 孫権, c = 劉備, d = 袁紹
# 返回值为空
def f():
return
print(f())
None
# pass 操作
def g():
pass
print(g())
None
# 布尔上下文中使用
def f():
return
def g():
pass
if f() or g():
print('yes')
else:
print('no')
no
可变长度参数
对于参数传递接受数量未知的情况,就需要编写一个可变参数传递设置。
# 计算平均数方法
def avg(a, b, c):
return (a + b + c) / 3
# 超过参数数量错误
avg(1, 2, 3, 4)
Traceback (most recent call last):
File "F:/PythonWorkProject/test.py", line 176, in <module>
avg(1, 2, 3, 4)
TypeError: avg() takes 3 positional arguments but 4 were given
# avg()使用可选参数进行定义,无限添加也行不通
def avg(a, b=0, c=0, d=0, e=0):
.
avg(1)
avg(1, 2)
avg(1, 2, 3)
avg(1, 2, 3, 4)
avg(1, 2, 3, 4, 5)
# 通过列表函数计算
def avg(a):
total = 0
for v in a:
total += v
return total / len(a)
avg([1, 2, 3])
2.0
avg([1, 2, 3, 4, 5])
3.0
参数元组打包、解包
函数定义中的参数名称以星号 ( * ) 开头时,表示参数可以以元组进行操作。函数调用中的任何相应参数都被打包到一个元组中,函数可以通过给定的参数名称引用该元组。
# 求平均数
def avg(*args):
total = 0
for i in args:
total += i
return total / len(args)
avg(1, 2, 3)
2.0
avg(1, 2, 3, 4, 5)
3.0
def avg(*args):
return sum(args) / len(args)
avg(1, 2, 3)
2.0
avg(1, 2, 3, 4, 5)
3.0
参数字典包装
函数定义中的参数名称以星号 ( ** ) 开头时,表示参数可以字典的打包和解包操作。函数定义中的参数前面加上双星号 ( ** ) 表示对应的参数是key=value对打包到字典中。
def f(**kwargs):
print(kwargs)
print(type(kwargs))
for key, val in kwargs.items():
print(key, '->', val)
f(曹操=1, 孫権=2, 劉備=3)
{
'曹操': 1, '孫権': 2, '劉備': 3}
<class 'dict'>
曹操 -> 1
孫権 -> 2
劉備 -> 3
位置参数列表和关键字参数列表混合使用。
将 *args 其视为可变长度的位置参数列表,以及 **kwargs 可变长度的关键字参数列表。按以下顺序指定它们,a, b, *args, **kwargs。
def f(a, b, *args, **kwargs):
print(F'a = {
a}')
print(F'b = {
b}')
print(F'args = {
args}')
print(F'kwargs = {
kwargs}')
f(1, 2, '曹操', '孫権', '劉備', '袁紹', x=100, y=200, z=300)
a = 1
b = 2
args = ('曹操', '孫権', '劉備', '袁紹')
kwargs = {
'x': 100, 'y': 200, 'z': 300}
函数调用中的多次解包
# 多次列表解包
def f(*args):
for i in args:
print(i)
a = [1, 2, 3]
t = (4, 5, 6)
s = {
7, 8, 9}
f(*a, *t, *s)
1
2
3
4
5
6
8
9
7
# 多次字典解包
def f(**kwargs):
for k, v in kwargs.items():
print(k, '->', v)
d1 = {
'a': 1, 'b': 2}
d2 = {
'x': 3, 'y': 4}
f(**d1, **d2)
a -> 1
b -> 2
x -> 3
y -> 4
关键字参数
参数必须由关键字指定的函数参数。
# 函数接受可变数量的字符串参数连接在一起,用点 ( ".") 分隔,打印到控制台
def concat(*args):
print(f'-> {
".".join(args)}')
concat('曹操', '孫権', '劉備', '袁紹')
-> 曹操.孫権.劉備.袁紹
与位置参数和解包方法连用
def concat(pre='-> ', *args):
print(f'{
pre}{
".".join(args)}')
concat('曹操', '孫権', '劉備', '袁紹')
曹操孫権.劉備.袁紹
# pre不是可选的,但始终必须包含在内,并且无法设置默认值。
concat(pre='//', '曹操', '孫権', '劉備', '袁紹')
File "<stdin>", line 1
SyntaxError: positional argument follows keyword argument
concat('曹操', '孫権', '劉備', '袁紹', pre='')
Traceback (most recent call last):
File "F:/PythonWorkProject/test.py", line 175, in <module>
concat('曹操', '孫権', '劉備', '袁紹', pre='')
TypeError: concat() got an unexpected keyword argument 'pre'
# 必须符合参数填充的位置
def concat(*args, pre='-> '):
print(f'{
pre}{
".".join(args)}')
concat('曹操', '孫権', '劉備', '袁紹', pre='')
曹操.孫権.劉備.袁紹
文档字符串
可以包含函数的用途、它需要什么参数、有关返回值的信息或其他有用的任何信息。
def avg(*args):
"""
返回数值列表的平均值
公式 = 总和 / 数量
"""
return sum(args) / len(args)
显示具体信息可以使用 .doc 方法。
print(avg.__doc__)
返回数值列表的平均值
公式 = 总和 / 数量
函数注解
Python 提供了一个附加功能,用于记录一个称为函数注释的函数。
可以使用类型对象进行注释。
def f(a: '<a>', b: '<b>') -> '<ret_value>':
pass
print(f.__annotations__)
{
'a': '<a>', 'b': '<b>', 'return': '<ret_value>'}
注释甚至可以是像列表或字典这样的复合对象。
def area(
r: {
'desc': '圆的半径',
'type': float
}) -> \
{
'desc': '圆的面积',
'type': float
}:
return 3.14159 * (r ** 2)
area(2.5)
19.6349375
area.__annotations__
{
'r': {
'desc': '圆的半径', 'type': <class 'float'>},
'return': {
'desc': '圆的面积', 'type': <class 'float'>}}
area.__annotations__['r']['desc']
'圆的半径'
area.__annotations__['return']['type']
<class 'float'>