函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。
简单来说,就是将某些频繁的操作抽象出来,形成一个指定的操作来重复利用,避免重复写代码,或将复杂的操作分工,一个阶段一个阶段的处理。
内置函数
python提供了一些内置函数,可参考该文档:内置函数
自定义函数
除了python提供的内置的函数,自己也可以根据需要编写函数
函数定义和调用
定义
def 函数名(参数列表):
执行代码块
return [expression]
调用
函数名(参数列表)
规则说明:
1.函数代码块以def关键词开头,后面接函数名称和英文圆括号()
2.圆括号内是需要传入的参数。参数可以传可以不传,传入的参数也是有格式要求的,后面再具体说明
3.函数内容以冒号起始,并且缩进。
4.使用return可以结束函数并给调用方返回相关内容。如果没有需要返回的内容,可以不用return语句,因为函数在运行完最后的语句(没碰到return)时,会默认执行return(即return None)
def print_content(content):
print(str(content))
result = print_content("好饿")
print("内容在函数中打印了,看下result的值")
print(f"result = {result}")
print_content("---------完结---------")
执行结果
好饿
内容在函数中打印了,看下result的值
result = None
---------完结---------
参数
参数传递
在python中,Number(数字)、String(字符串)、Tuple(元组)是不可变对象,List(列表)、Dictionary(字典)、Set(集合)等是可变对象(参考python基本数据类型 的浅拷贝和深拷贝)。我们在定义变量内容时,是var1 = xxx
,变量名var1可以理解为一个指针,该指针指向(即保存)了xxx在内存中的地址,当前所说的可变和不可变,是针对xxx的,不是针对变量名称。所以不可变对象,是开辟了内存空间,这部分内存空间存储的内容不能变动,可变对象是这部分内存空间存储的内容可以变动,而变量只是指向这个内存空间地址,或者指向那个内存空间地址。
1.参数传入了可变对象的话,则函数对参数的改动只在函数内生效,函数外不生效
def fun1(var1):
print("调用中1:", var1, id(var1))
# var1只是修改了变量var1指向的内容,可以理解为该函数定义了个变量var1,并指向了存储内容为4的对象地址
var1 = 4
print("调用中2:", var1, id(var1))
var2 = 2
print("调用前:", var2, id(var2))
fun1(var2)
print("调用后:", var2, id(var2))
执行结果:
调用前: 2 1536671678736
调用中1: 2 1536671678736
调用中2: 4 1536671678800
调用后: 2 1536671678736
2.若参数传入的是可变对象,则函数内对对象的改变,函数外也生效
def fun1(var1):
print("调用中1:", var1, id(var1))
var1[0] = 0
print("调用中2:", var1, id(var1))
var2 = [2, 4, 6]
print("调用前:", var2, id(var2))
fun1(var2)
print("调用后:", var2, id(var2))
执行结果:
调用前: [2, 4, 6] 2653420713088
调用中1: [2, 4, 6] 2653420713088
调用中2: [0, 4, 6] 2653420713088
调用后: [0, 4, 6] 2653420713088
参数类型
调用函数时可使用的正式参数类型:必需参数、关键字参数、默认参数、不定长参数
必需参数
必备参数须以正确的顺序传入函数。调用时参数的数量必须与定义时保持一致,否则调用时会报错
正确示例:
def sum_scores(Chinese, Math, English):
print(f"Chinese: {Chinese}\nMath: {Math}\nEnglish: {English}")
print(f"总分为:{float(Chinese) + float(Math) + float(English)}分")
sum_scores(70, 85, 66) # 与定义函数时的参数数量保持一致,都是3个参数:Chinese, Math, English
执行结果过为
Chinese: 70
Math: 85
English: 66
总分为:221.0分
从以上结果可以看出,传入的参数按照调用时的顺序依次赋值,如Chinese为70,Math为85,English为66,按照顺序依次对应。
若调用时多加了一个参数,如:sum_scores(70, 85, 66, 43)
时会有如下报错:
Traceback (most recent call last):
File "E:\hogwart\demo\demo.py", line 4, in <module>
sum_scores(70, 85, 66, 43)
TypeError: sum_scores() takes 3 positional arguments but 4 were given
调用时少加了一个参数:sum_scores(70, 85)
,会有如下报错:
Traceback (most recent call last):
File "E:\hogwart\demo\demo.py", line 4, in <module>
sum_scores(70, 85)
TypeError: sum_scores() missing 1 required positional argument: 'English'
关键字参数
关键字参数和函数调用关系紧密,函数调用使用关键字参数来确定传入的参数值。
函数调用时,根据参数名=值
的方式来传递指定的参数
def sum_scores(Chinese, Math, English):
print(f"Chinese: {Chinese}\nMath: {Math}\nEnglish: {English}")
print(f"总分为:{float(Chinese) + float(Math) + float(English)}分")
进行如下调用:
sum_scores(Math=78, English=90, Chinese=87)
执行结果:
Chinese: 87
Math: 78
English: 90
总分为:255.0分
从调用语句可以看出,调用参数没有按照函数定义的顺序来排序传入,是以变量名=值
来传入的,这种传入格式可以不用考虑函数定义的顺序,但是变量名需要注意与函数定义时保持一致。
默认参数
在函数定义时给参数设置默认值,即是默认参数,在调用时,默认参数若没有传入值,则使用默认值,若有传入值,则使用传入的值。
def sum_scores(Chinese, Math=60, English=0):
print(f"Chinese: {Chinese}\nMath: {Math}\nEnglish: {English}")
print(f"总分为:{float(Chinese) + float(Math) + float(English)}分")
sum_scores(Chinese=87)
print("------------------------------")
sum_scores(77, English=83)
执行结果
Chinese: 87
Math: 60
English: 0
总分为:147.0分
------------------------------
Chinese: 77
Math: 60
English: 83
总分为:220.0分
函数定义时,默认参数必须放在非默认参数之后,毕竟参数可以按照顺序传入,默认参数已经有默认值作为“保底”,为了保证参数都有传入值,则在函数定义时,需要将非默认参数排在默认参数的前面。
示例:
def sum_scores(Chinese=60, Math, English=0):
print(f"Chinese: {Chinese}\nMath: {Math}\nEnglish: {English}")
print(f"总分为:{float(Chinese) + float(Math) + float(English)}分")
sum_scores(87, 69, 73)
程序执行报错了,提示了非默认参数后面跟着默认参数
File "E:\hogwart\demo\demo.py", line 1
def sum_scores(Chinese=60, Math, English=0):
^^^^
SyntaxError: non-default argument follows default argument
不定长参数
不定长参数也称可变参数,传入函数中的实际参数可以是任意多个。
常见的形式有*args
和**kwargs
元组形式
def 函数名([已知参数,] *元组不定长参数名):
执行语句....
# 以元组的形式使用元组不定长参数名
执行语句....
即在函数定义时,使用*
来定义元组类型的不定长参数,在函数调用时,按照函数定义时的参数顺序和参数名称分配好之后,剩下的未指定参数名的参数,均作为元组不定长参数的元素。
示例:
def print_content(kind, *data, other):
print(f"类型:{kind}")
print(f"类型数据:{data}")
print(type(data))
print(other)
print_content("水果", "西瓜", "苹果", "葡萄", "西红柿", "香蕉", other="ase")
执行结果:
类型:水果
类型数据:('西瓜', '苹果', '葡萄', '西红柿', '香蕉')
<class 'tuple'>
ase
元组形式的不定长参数,需要放在函数定义的参数列表末尾,给必需参数、关键字参数分配好之后,剩下的无分配的,就是不定长参数了。一般情况下,建议在函数定义时,将不定长参数放置在列表末尾,方便维护管理,比如以上代码,如果没有指定other的值,则会报错(因为在调用函数时,默认按照参数顺序进行传入,排在不定长参数后面的参数就需要按照关键字参数的形式进行指明)。
若调用语句如print_content("水果", "西瓜", "苹果", "葡萄", "西红柿", "香蕉", "ase")
,则报错如下(缺少other参数的传入):
Traceback (most recent call last):
File "E:\hogwart\demo\demo.py", line 7, in <module>
print_content("水果", "西瓜", "苹果", "葡萄", "西红柿", "香蕉", "ase")
TypeError: print_content() missing 1 required keyword-only argument: 'other'
字典形式
def 函数名([已知参数,] **kwargs):
执行语句....
# 以字典的形式使用kwargs
执行语句....
在函数定义时,使用**
来修饰字典形式的不定长参数。函数调用时,则以键=值
的方式进行传入。python会将键名不在已知参数的参数名的作为字典不定长参数的一个键值对。
示例:
def print_content(Chinese=0, Math=0, English=0, **other):
print(f"主科成绩:语文({Chinese})、数学({Math})、英语({English})")
print(f"副科成绩:{other}")
print(type(other))
print_content(88, 93, 79, sport=80, phy=77)
print("----------------------------------")
print_content(88, English=67, phi=90, Math=70)
执行结果为:
主科成绩:语文(88)、数学(93)、英语(79)
副科成绩:{'sport': 80, 'phy': 77}
<class 'dict'>
----------------------------------
主科成绩:语文(88)、数学(70)、英语(67)
副科成绩:{'phi': 90}
<class 'dict'>
* 和 **
有时候需要将列表、元组、集合或字典中的元素传入不定长参数中,python提供了更方便的方法。
如果列表、元组、集合或字典直接传入不定长参数中时,传入时是什么类型,传入后也是以同样的类型作为不定长参数的元素。
def print_seq(*data):
print("-------------------")
print(f"值:{data}")
def print_dict(**data):
print("-------------------")
print(f"值:{data}")
print_seq(["西瓜", "苹果"], "其他")
print_seq(("香蕉", "山竹"), "其他")
print_dict(param1={"key1": "value1", "key2": "value2"}, param2="other")
执行结果为:
-------------------
值:(['西瓜', '苹果'], '其他')
-------------------
值:(('香蕉', '山竹'), '其他')
-------------------
值:{'param1': {'key1': 'value1', 'key2': 'value2'}, 'param2': 'other'}
若想将元素传入不定长参数中,可以在列表、集合、元组等序列前加上*
,可以在字典前加上**
示例:
def print_seq(*data):
print("-------------------")
print(f"值:{data}")
def print_dict(**data):
print("-------------------")
print(f"值:{data}")
print_seq(*["西瓜", "苹果"], "其他")
print_seq(*("香蕉", "山竹"), "其他")
print_dict(**{"key1": "value1", "key2": "value2"}, param2="other")
print_dict(**{"key1": "value1", "key2": {"key22": "value22", "key23": "value23"}}, param2="other")
执行结果:
-------------------
值:('西瓜', '苹果', '其他')
-------------------
值:('香蕉', '山竹', '其他')
-------------------
值:{'key1': 'value1', 'key2': 'value2', 'param2': 'other'}
-------------------
值:{'key1': 'value1', 'key2': {'key22': 'value22', 'key23': 'value23'}, 'param2': 'other'}
注意:*
和 **
只在函数调用和定义的时候生效,在赋值时是会报错的
返回值
有些函数需要将执行结果返回给调用方,即return[ xxx]
,return语句退出并将执行结果返回给调用方,若没有return语句,即函数中的语句执行到末尾,默认会执行return None
返回值示例:
def is_adult(age):
if int(age) > 18:
return "成年人"
else:
return "未成年人"
result = is_adult(34)
print("result = ", result)
执行结果:
result = 成年人
有时候也不会设置返回值,则默认返回None
def is_adult(age):
if int(age) > 18:
print("成年人")
else:
print("未成年人")
result = is_adult(34)
print("result = ", result)
执行结果:
成年人
result = None
匿名函数
格式:变量名 = lambda [arg1 [,arg2,.....argn]]:expression
简单点理解,就是arg为传入的参数,expression为返回的结果。
比如计算3个数值的和,正常使用函数方法如下:
def sum(a, b, c):
result = a + b +c
return result
print(sum(45, 23, 66))
使用匿名函数的方法为:
result = lambda a, b, c: a+b+c
print(result(45, 23, 66))
2者的执行结果一样:
134
其他
打印函数注释
在调用函数时,我们需要了解被调函数的逻辑、参数和使用的注意事项,一般这些内容是在函数comments内容中(我们写函数的时候也要养成习惯,在函数定义时使用多行注释来说明该函数的使用事项)
def sum_scores(Chinese=0, Math=0, English=0):
"""
计算语文、数学、英语的的总分(若未传入,则该科分数默认为零分进行计算)并打印
注意:该部分为函数注释说明,即comments
:param Chinese: 语文分数
:param Math: 数学分数
:param English: 英语分数
:return: 无返回
"""
print(f"Chinese: {Chinese}\nMath: {Math}\nEnglish: {English}")
print(f"总分为:{float(Chinese) + float(Math) + float(English)}分")
# 打印函数sum_scores()的comments内容
print(sum_scores.__doc__)
print("**********************")
help(sum_scores)
print("**********************")
# 调用函数
sum_scores(87, 69, 73)
执行结果:
计算语文、数学、英语的的总分(若未传入,则该科分数默认为零分进行计算)并打印
注意:该部分为函数注释说明,即comments
:param Chinese: 语文分数
:param Math: 数学分数
:param English: 英语分数
:return: 无返回
**********************
Help on function sum_scores in module __main__:
sum_scores(Chinese=0, Math=0, English=0)
计算语文、数学、英语的的总分(若未传入,则该科分数默认为零分进行计算)并打印
注意:该部分为函数注释说明,即comments
:param Chinese: 语文分数
:param Math: 数学分数
:param English: 英语分数
:return: 无返回
**********************
Chinese: 87
Math: 69
English: 73
总分为:229.0分
类型注解
有时候,对函数(或方法)的传入参数和返回内容进行类型注解,即指定是什么类型的数据,可以让调用者更直观的了解函数(或方法),增强了代码的可读性,编写时IDE工具也会有相应的方法展示。
格式:
def fun1(param1: 类型1, param2) -> 类型2:
statement
...
statement
示例:
class subject:
scores = {}
def __init__(self, **kwargs):
self.scores = kwargs
def sum(name: str, age: int, subject_info: subject):
sum = 0.0
for score in subject_info.scores.values():
sum += float(score)
print(f"{name}今年{age}岁,考分总分为:{sum}")
sub = subject(语文=82, 数学=88, 英语=79)
sum("张三", 16, sub)
# 类型注解只是做提示用,并不会强制传入参数是指定的类型,在函数内部具体执行的时候有报错才会报错
# 如以下的调用,函数指定是age为int类型,但是传入为字符串的时候,也执行成功了。
sub_li = subject(语文=82, 数学=90, 英语=75)
sum("李四", "17", sub_li)
执行结果:
张三今年16岁,考分总分为:249.0
李四今年17岁,考分总分为:247.0
也可以给类型注解起别名
# 为类型起别名,如下,list[float]表示元素均为字符串str的列表数据
Name = list[str]
def nums(pre: str, name: Name) -> Name:
l = []
for value in name:
l.append(f"{pre}, {value}")
return l
data = ["Marry", "Jhon", "Henrry"]
print(data)
print(nums("hello", data))
print(data)
执行结果:
['Marry', 'Jhon', 'Henrry']
['hello, Marry', 'hello, Jhon', 'hello, Henrry']
['Marry', 'Jhon', 'Henrry']