函数就是带名字的代码块,用于完成特定的任务。要执行函数定义的特定任务,只需调用该函数。
1、函数的定义
定义函数可以使用def关键字,def后面是自定义的函数名,函数名后面的括号中用于传入执行该函数所需的信息(函数参数),即使不需要任何参数也不能省略括号,最后以冒号结尾。
紧跟在def语句之后的缩进代码块为函数体,函数体中可以用三引号括起的为文档字符串(docstring)的注释,用于描述该函数是做什么的,python使用它们生成程序中函数的文档。
#定义函数
def my_print():
"""打印欢迎"""
print('Hello!')
#调用函数
my_print()
#结果:
Hello!
代码中的函数名为my_print,且无需任何参数,定义了一个专门的打印欢迎语句的函数。
1.1 向函数传递参数
改进一下上面的代码,向函数传入参数:
#定义函数
def my_print(name):
print('Hello!' + name.title())
#调用函数
my_print('jack')
#结果:
Hello!Jack
#定义函数
def my_print(name):
print('Hello!' + name.title())
#调用函数
my_print('rose')
#结果:
Hello!Rose
1.2 实参和形参
定义函数时,括号内的参数称为形参,即函数执行中所需的信息(如代码中的name)。而实参则是调用函数时传递给函数的信息,即调用函数时括号中的参数(如代码中的'jack')。
#定义函数
def my_print(name):
print('Hello!' + name.title())
#调用函数
my_print('jack')
#结果:
Hello!Jack
2、传递参数
定义函数中可能有多个形参,调用函数时也可能有多个实参。向函数传递参数的方式有很多种,如位置参数,关键字参数,以及列表和字典。
2.1 位置参数
位置参数,要求实参的顺序与形参的顺序相同,调用函数时,将函数调用中的每一个实参都关联到函数定义中的每一个形参。
#定义函数
def my_print(name,age):
print('Hello!' + name.title() + '.')
print('Your age is ' + str(age))
#调用函数
my_print('rose',21)
#结果:
Hello!Rose.
Your age is 21
函数定义了两个参数(name和age),调用函数时,也需传入两个实参。更多的参数也是类似,不过一定要注意形参的顺序和实参的顺序要一致。
2.2 关键字参数
关键字参数是以名称-值对传递给函数的,直接在调用函数时的实参中将参数名称与值关联起来了,因此传递参数时不会混淆。关键字参数无需考虑函数调用中的实参顺序,指明了函数调用中各个值的用途。
#定义函数
def my_print(name,age):
print('Hello!' + name.title() + '.')
print('Your age is ' + str(age))
#调用函数
my_print(age=21,name='jack')
#结果:
Hello!Jack.
Your age is 21
关键字实参的顺序无关紧要,Python知道各个实参应该传递给哪个形参。
2.3 默认值参数
定义函数时,可以给每个形参指定默认值。如果在调用函数时,给形参传递了具体的实参,则使用具体的实参;如果函数调用时没有指定特定的实参传递给形参,则使用形参的默认值。
#定义函数
def my_print(name,age=21):
print('Hello!' + name.title() + '.')
print('Your age is ' + str(age))
#调用函数
my_print('jack',20)
my_print(('loss'))
#结果:
Hello!Jack.
Your age is 20
Hello!Loss.
Your age is 21
代码中,第一次调用函数时给定了形参age具体的实参20,因此使用该实参传递给形参age;第二次调用函数时,并没有给定形参age的具体实参,因此直接使用形参的默认值21。
在这个函数的定义中,使用了默认值的形参age放在没有使用默认值的形参name的后面。
因为Python将这些没有默认值的形参视为位置参数,使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的实参,让Python能够正确地解读其他位置实参。否则如图报错:
#定义函数
def my_print(name='wumingshi',age):
print('Hello!' + name.title() + '.')
print('Your age is ' + str(age))
#调用函数
my_print('jack',20)
my_print(21)
#结果:
File "E:\mypro.py", line 2
def my_print(name='wumingshi',age):
^
SyntaxError: non-default argument follows default argument
3、返回值
函数可以处理数据,并返回一个或一组值,被称为返回值。在函数中,可以使用return语句将返回值返回到调用函数的代码行,继续运行。返回值可以将程序中大部分繁重工作转移至函数中完成,简化主程序。
3.1 返回简单值
#定义函数
def my_sum(a,b):
"""求两数之和"""
return a + b
#调用函数
s = my_sum(3,9)
print(s)
#结果:
12
该定义函数返回输入的两数之和。
3.2 返回不同值
在函数调用中,可以根据不同的输入参数情况,返回不同的值。
#定义函数
def my_fun(firstname,lastname,middlename=''):
"""返回全名"""
if middlename:
fullname = firstname + ' ' + middlename + ' ' + lastname
else:
fullname = firstname + ' ' + lastname
return fullname.title()
#调用函数
s = my_fun('jim','hex','lee')
print(s)
s = my_fun('jim','hex')
print(s)
#结果:
Jim Lee Hex
Jim Hex
3.3 返回字典
函数可以返回任何类型的值,包括列表和字典等比较复杂的数据结构。
#定义函数
def my_fun(name,age):
"""返回字典"""
person = {'name': name,'age': age}
return person
#调用函数
s = my_fun('Jack',22)
print(s)
#结果:
{'name': 'Jack', 'age': 22}
3.4 结合while循环
#定义函数
def my_print(name):
return 'Hello!' + name.title()
#调用函数
my_print('jack')
while True:
print('Please input your name:(enter "q" to quit) ')
yname = input('Your name: ')
if yname == 'q':
break
fname = my_print(yname)
print(fname)
#结果:
Please input your name:(enter "q" to quit)
Your name: liang
Hello!Liang
Please input your name:(enter "q" to quit)
Your name: q
4、传递列表
有时候,向函数传递列表很有用,列表元素可能是字符串、数字或更复杂的对象(如字典)。将列表传递给函数后,函数就能直接访问其内容。
#定义函数
def greet(names):
"""问候每个用户"""
for name in names:
print('Hello, ' + name)
usernames = ['Jack', 'Bob', 'Mike']
greet(usernames)
#结果:
Hello, Jack
Hello, Bob
Hello, Mike
4.1 在函数中修改列表
将列表传递给函数之后,函数就可以对其进行修改,所做的修改是永久性的,能够高效地处理大量的数据。
#定义函数
def trans(uncom, com):
"""将一个列表的元素转移到另一个列表"""
while uncom:
temp = uncom.pop()
com.append(temp)
def show(com):
for each in com:
print(each)
uncom = [4,3,6,4,2,1]
com = []
trans(uncom, com)
show(com)
#结果:
1
2
4
6
3
4
定义的第一个函数包括两个形参,负责将一个列表的元素转移到另一个列表,第二个函数只有一个参数,负责将新列表中的每个元素打印。使用函数,使程序更容易扩展和维护,每个函数都只负责一项具体的工作。
4.2 禁止函数修改列表
有时候,需要禁止函数修改列表,即需要保留原来的列表。这种情况下,可以向函数传递列表的副本而不是列表本身,这样函数只修改了副本,而不影响原列表。
要传递副本,可以使用列表切片:
function_name(list_name[:])
在上述代码中,如果不想改变原列表,可以这样调用函数:
trans(uncom[:], com)
5、传递任意数量的实参
有时候,并不知道函数需要接收多少个实参,Python允许函数从调用语句中收集任意数量的实参。使用收集参数,可以将收集到的所有参数封装到一个元组中。
#定义函数
def make_juice(*ingredients):
"""打印原料"""
print(ingredients)
make_juice('apple')
make_juice('banana','apple','orange')
#结果:
('apple',)
('banana', 'apple', 'orange')
还可以对收集的参数遍历:
#定义函数
def make_juice(*ingredients):
"""打印原料"""
print('\nThe ingredients: ')
for ingredient in ingredients:
print('-' + ingredient)
make_juice('apple')
make_juice('banana','apple','orange')
#结果:
The ingredients:
-apple
The ingredients:
-banana
-apple
-orange
5.1 集合位置参数和收集参数
如果要让函数接收不同类型的参数,必须在定义函数时将收集参数放在参数列表的最后。Python先匹配位置参数和关键字参数,再将余下的参数都收集到最后一个收集参数中。
#定义函数
def make_juice(size, *ingredients):
"""打印原料"""
print('\nThe ingredients of the ' + size + ' juice: ')
for ingredient in ingredients:
print('-' + ingredient)
make_juice('middle','apple')
make_juice('large','banana','apple','orange')
#结果:
The ingredients of the middle juice:
-apple
The ingredients of the large juice:
-banana
-apple
-orange
上述代码中,在函数定义中,将收集参数(*ingredient)放在参数列表的最后,调用函数时,将第一个实参传递给形参size,将余下的实参全部打包给形参*ingredient,并存储在元组ingredient中。
5.2 任意数量的关键字参数
有时,需要接收任意数量的实参,但预先不知道传递给函数的会是什么样的信息。这时,可以将函数定义为可以接收任意数量的键-值对——调用语句提供多少函数就接收多少。
#定义函数
def users(fname,lname,**other):
"""创建一个字典,其中包含用户的其他信息"""
users_information= {}
users_information['firstname'] = fname
users_information['lastname'] = lname
for key, value in other.items():
users_information[key] = value
return users_information
user1 = users('zhang','liang',age = 22, grade = 95)
print(user1)
#结果:
{'firstname': 'zhang', 'lastname': 'liang', 'age': 22, 'grade': 95}
上述定义的函数接收两个参数(fname和lname),同时还接收任意数量的关键字参数。形参**other的两个星号让Python创建一个名为other的空字典,并将接收的所有名称-值对都封装到这个字典中,在函数中,可以像访问字典一样访问other的名称-值对。
调用这个函数时,可以传递任意多个关键字参数:
#定义函数
def users(fname,lname,**other):
"""创建一个字典,其中包含用户的其他信息"""
users_information= {}
users_information['firstname'] = fname
users_information['lastname'] = lname
for key, value in other.items():
users_information[key] = value
return users_information
user1 = users('zhang','liang',age = 22, grade = 95, subject = 'math')
print(user1)
#结果:
{'firstname': 'zhang', 'lastname': 'liang', 'age': 22, 'grade': 95, 'subject': 'math'}
编写函数时,可以各种方式混合使用位置参数、关键字参数和收集参数。要正确使用这些参数类型并能在合适的情形下高效地使用,需要一定的练习。
6、将函数存储在模块中
函数的优点是,使用函数可以将代码块和主程序分离。还可以更进一步,将函数存储在独立的文件中,这个文件被称为模块。使用import语句可以将模块导入到主程序中,允许当前运行的程序中使用模块中的代码。
将函数存储在独立的文件中后,可以与其他程序员共享这些文件而不是整个程序,知道如何导入模块还能使用其他程序员编写的函数库。
6.1 导入整个模块
要导入模块,得先创建模块。模块是扩展名为.py的文件,包含要导入到程序中的代码。先创建一个名为juice.py的模块,包含函数make_juice():
juice.py
#定义函数
def make_juice(size, *ingredients):
"""打印原料"""
print('\nThe ingredients of the ' + size + ' juice: ')
for ingredient in ingredients:
print('-' + ingredient)
接着,在另一个.py文件中导入这个模块,再使用这个函数make_juice():
import juice
juice.make_juice('middle','apple')
juice.make_juice('large','banana','apple','orange')
#结果:
The ingredients of the middle juice:
-apple
The ingredients of the large juice:
-banana
-apple
-orange
python在执行import juice这句代码时,将打开文件juice.py,并将其中的所有函数都复制到这个程序中,然后可以在这个文件中使用juice.py中的所有函数。
要调用被导入模块的函数,可以指定模块名称和函数名,并用句号.分隔它们,如上述代码。
6.2 导入特定的函数
还可以直接导入模块中特定的函数,而不是导入整个模块:
from module_name import function_name
通过逗号分隔函数名,可以导入任意数量的函数:
from module_name import function_0, function_1, function_2
前面的示例可以改为:
from juice import make_juice
make_juice('middle','apple')
make_juice('large','banana','apple','orange')
这时,调用函数时,因为我们在import语句中显示地导入了函数make_juice,因此调用时无需指定模块名。
6.3 使用as给函数指定别名
如果要导入的函数名与当前程序中的现有名称有冲突,或者函数的名称太长,可以重新指定函数的别名,类似于外号,即该模块中的函数在当前程序中的名字。
from juice import make_juice as mj
mj('middle','apple')
mj('large','banana','apple','orange')
上面的代码中,import语句将make_juice()函数重命名为mj();在这个程序中,需要调用make_juice()函数时,都可以简写为mj(),python将运行juice模块中的make_juice()函数,避免与这个程序中可能的同名函数混淆。
from module_name import function_name as fn
6.4 使用as给模块指定别名
还可以给模块指定别名,通过给模块指定更简洁的别名,可以方便地调用模块中的函数。
import juice as je
je.make_juice('middle','apple')
je.make_juice('large','banana','apple','orange')
通用语法为:
import module_name as mn
6.5 导入模块中的所有函数
使用星号(*)可以导入模块中的所有函数:
from mofule_name import *
from juice import *
make_juice('middle','apple')
make_juice('large','banana','apple','orange')
import语句的*可以将模块中的所有函数都复制到当前程序中,由于导入了所有函数,因此调用时不用指定模块名,可以直接使用函数名。
然而,使用他人编写的大型模块时,不建议使用这种导入方法:模块中可能有函数名称与你的项目中使用的名称相同,可能导致错误。(python遇到多个名称相同的函数或变量,进而覆盖函数)
最佳的导入方法为,只导入你需要的模块,或者导入整个模块并使用模块名加函数名的调用方法。这种方法让代码更清晰,易于阅读和理解。
7、函数编写理念
- 编写函数时,给函数名指定描述性名称,且只在其中使用小写字母和下划线,描述性名称能帮助自己和他人了解函数的大概功能。给模块命名时也是如此。
- 每个函数都应包含说明其功能的注释,注释紧跟在定义后面,并采用文档字符串的格式。
- 给形参指定默认值时,等号两边不要空格,实参亦是如此:
deg function_name(parameter_0,parametrt_1='default valur')
- 定义函数时,代码行的长度不要超过79字符
def function_name( parameter_0, parameter_1, parameter_2, parameter_3, parameter_4, paramerer_5): function body...
- 如果模块包含多个函数,可以使用两个空行将两个相邻的函数分开。
- 所有import语句都应放在文件的开头,除非文件开头使用了注释来描述整个程序。