第八章:函数
- 函数(function)是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
- 要执行函数定义的特定任务,可调用该函数
- 函数能提高应用的模块性,和代码的重复利用率。在程序中多次执行同一任务,可多次调用该函数,无需反复编写
- python内置函数:print()
- 自定义函数
8.1 定义函数
你可以定义一个由自己想要功能的函数,以下是简单的规则:
- 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号()。
- 任何传入参数和自变量必须放在圆括号中间。圆括号之间可以用于定义参数。
- 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。
- 函数内容以冒号起始,并且缩进。
- return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
def functionname( parameters ):
"函数_文档字符串"
function_suite
return [expression]
def greet_user(): #定义函数
"显示简单的问候语"
print("Hello!")
greet_user() # 调用函数
Hello!
8.1.2 向函数传递信息(参数传递)
def greet_user(username):
"显示简单的问候语"
print(f"Hello, {username.title()}")
greet_user('jesse') # 函数调用
#greet_user()
Hello, Jesse
8.1.3 形参和实参
- 形参(parameter):函数完成工作所需的参数 - username
- 实参(argument):调用函数时传递给函数的参数 - ‘jesse’
8.2 传递实参
- 函数定义中可以包含多个形参,因此函数调用时也可能包含多个实参
- 传递实参的方法:
- 位置实参
- 关键字实参
- 列表
- 字典
8.2.1 位置实参
- 基于实参的位置顺序 将函数调用中的实参关联到函数定义中的形参
- 顺序不对容易发生混淆
任务: 定义一个显示宠物信息的函数
def describe_pet(animal_type, pet_name):
"显示宠物信息"
print(f"\nI have a {animal_type}." )
print(f"My {animal_type}'name is {pet_name.title()}.")
describe_pet('hamster','harry')
I have a hamster.
My hamster'name is Harry.
I have a dog.
My dog'name is Willie.
8.2.2 关键字实参
- 传递给函数的“名称-值对”,直接在实参中将名称和值关联起来,不会混淆
- 无需考虑函数调用中实参顺序,还清楚的指出函数调用中各个值的用途
def describe_pet(animal_type, pet_name):
"显示宠物信息"
print(f"\nI have a {animal_type}." )
print(f"My {animal_type}'name is {pet_name.title()}.")
describe_pet(animal_type= 'hamster', pet_name='harry')
I have a hamster.
My hamster'name is Harry.
def describe_pet(animal_type, pet_name):
"显示宠物信息"
print(f"\nI have a {animal_type}." )
print(f"My {animal_type}'name is {pet_name.title()}.")
describe_pet( pet_name='harry',animal_type= 'hamster')
I have a hamster.
My hamster'name is Harry.
8.2.3 默认值
- 编写函数时,可给每个形参指定默认值(default value)
- 调用函数中给形参提供了实参,Python将使用指定的实参值,否则将使用形参的默认值
任务: 将形参animal_type的默认值设置为‘dog’
# 默认值
def describe_pet(pet_name, animal_type= 'dog'):
"显示宠物信息"
print(f"\nI have a {animal_type}." )
print(f"My {animal_type}'name is {pet_name.title()}.")
describe_pet(pet_name='willie')
I have a dog.
My dog'name is Willie.
# 忽略默认值
def describe_pet(pet_name, animal_type= 'dog'):
"显示宠物信息"
print(f"\nI have a {animal_type}." )
print(f"My {animal_type}'name is {pet_name.title()}.")
describe_pet( pet_name='harry',animal_type= 'hamster')
I have a hamster.
My hamster'name is Harry.
8.2.4 等效的函数调用
- 混合使用位置实参,关键字实参和默认值
- 使用哪种调用方式无关紧要
def describe_pet(pet_name, animal_type= 'dog'):
"显示宠物信息"
print(f"\nI have a {animal_type}." )
print(f"My {animal_type}'name is {pet_name.title()}.")
# animal_type 为默认值 'dog'
describe_pet('willie') #位置实参
describe_pet(pet_name='willie') #关键字实参
I have a dog.
My dog'name is Willie.
I have a dog.
My dog'name is Willie.
def describe_pet(pet_name, animal_type= 'dog'):
"显示宠物信息"
print(f"\nI have a {animal_type}." )
print(f"My {animal_type}'name is {pet_name.title()}.")
# animal_type 不为默认值
describe_pet('harry', 'hamster') #位置实参
describe_pet( pet_name='harry',animal_type= 'hamster') #关键字实参
describe_pet(animal_type= 'hamster', pet_name='harry') #关键字实参
I have a hamster.
My hamster'name is Harry.
I have a hamster.
My hamster'name is Harry.
I have a hamster.
My hamster'name is Harry.
8.2.5 避免实参错误
def describe_pet(pet_name, animal_type):
"显示宠物信息"
print(f"\nI have a {animal_type}." )
print(f"My {animal_type}'name is {pet_name.title()}.")
describe_pet()
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-15-42ca51a5424d> in <module>
4 print(f"My {animal_type}'name is {pet_name.title()}.")
5
----> 6 describe_pet()
TypeError: describe_pet() missing 2 required positional arguments: 'pet_name' and 'animal_type'
8.3 返回值
- 函数并非总是直接显示输出,还可以处理一些数据,并返回一个或一组值
- 函数返回的值称作返回值(return statement )
- 在函数中,可使用return语句将值返回到调用函数的代码行
- 返回值可简化主程序
8.3.1 返回简单值
任务: 接受名和姓并返回整洁的姓名
def get_formatted_name(first_name, last_name):
"返回整洁的姓名"
full_name = f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi','hendix')
print(musician)
Jimi Hendix
8.3.2 让实参变成可选的
- 让实参变成可选的,调用函数时只需在必要是提供额外的信息
- 可使用默认值让实参变成可选的
def get_formatted_name(first_name,middle_name, last_name):
"返回整洁的姓名"
full_name = f"{first_name} {middle_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi','lee', 'hendix')
print(musician)
Jimi Lee Hendix
# 并非所有的人都有中间名,若只提供名和姓,调用函数将不能正确运行
# 需将中间名变成可选的
# 给middle_name 指定一个空字符串,并在用户不提供中间名是不使用该形参
def get_formatted_name(first_name, last_name, middle_name=''):
"返回整洁的姓名"
if middle_name:
full_name = f"{first_name} {middle_name} {last_name}"
else:
full_name = f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi', 'hendix')
print(musician)
musician = get_formatted_name('jimi', 'hendix','lee')
print(musician)
Jimi Hendix
Jimi Lee Hendix
8.3.3 返回字典
- 函数可以返回任何数据类型,包括列表和字典等复杂数据结构
任务: 接受姓名的组成部分,并返回一个表示人的字典
def biuld_person(first_name, last_name):
"返回一个字典,包含有关一个人的信息"
person = {'first': first_name, 'last': last_name}
return person
musician = biuld_person('jimi', 'hendix')
print(musician)
Jimi Hendix
def biuld_person(first_name, last_name, age = None):
"返回一个字典,包含有关一个人的信息"
person = {'first': first_name, 'last': last_name,}
if age:
person['age'] = age
return person
musician = biuld_person('jimi', 'hendix', age = 27)
print(musician)
{'first': 'jimi', 'last': 'hendix', 'age': 27}
8.3.4 结合使用函数和while循环
任务: 结合使用函数 get_formatted_name()和while循环,和用户打招呼
def get_formatted_name(first_name, last_name):
"返回整洁的姓名"
full_name = f"{first_name} {last_name}"
return full_name.title()
while True:
print("\nPlease tell me your name:")
f_name = input("First name: ")
l_name = input("Last name: ")
formatted_name = get_formatted_name(f_name, l_name)
print(f"\nHello, {formatted_name}!")
# 无限循环,无法退出
def get_formatted_name(first_name, last_name):
"返回整洁的姓名"
full_name = f"{first_name} {last_name}"
return full_name.title()
while True:
print("\nPlease tell me your name:")
print("(enter 'q' at any time to quit)")
f_name = input("First name: ")
if f_name == 'q':
break
l_name = input("Last name: ")
if l_name == 'q':
break
formatted_name = get_formatted_name(f_name, l_name)
print(f"\nHello, {formatted_name}!")
Please tell me your name:
(enter 'q' at any time to quit)
First name: eric
Last name: matthes
Hello, Eric Matthes!
Please tell me your name:
(enter 'q' at any time to quit)
First name: q
8.4 传递列表
- 将列表传递给函数后,函数就能直接访问其内容
任务: 将包含名字的列表传递给一个名为greet_users()的函数,该函数问候列表中的每个人
def greet_users(names):
"向列表中用户发出简单问候"
for name in names:
msg = f"Hello, {name.title()}!"
print(msg)
usernames = ['hannah','ty','margot']
greet_users(usernames)
Hello, Hannah!
Hello, Ty!
Hello, Margot!
8.4.1 在函数中修改列表
- 将列表传递给函数后,函数可以对其修改
- 在函数中对列表的修改是永久性的
任务: 将打印的设计存储在一个列表中,打印后将其移动到另一个列表中
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []
while unprinted_designs:
current_design = unprinted_designs.pop()
print(f"Printing model: {current_design}")
completed_models.append(current_design)
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
Printing model: dodecahedron
Printing model: robot pendant
Printing model: phone case
The following models have been printed:
dodecahedron
robot pendant
phone case
def print_models(unprinted_designs, completed_models):
while unprinted_designs:
current_design = unprinted_designs.pop()
print(f"Printing model: {current_design}")
completed_models.append(current_design)
def show_completed_models(completed_models):
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
Printing model: dodecahedron
Printing model: robot pendant
Printing model: phone case
The following models have been printed:
dodecahedron
robot pendant
phone case
8.4.2 禁止函数修改列表
- 将列表的副本传递给函数,函数所做的修改只影响副本,原件不受影响
- 传递列表副本——function_name(list_name[:])
- 传递列表——function_name(list_name)
8.5 传递任意数量的实参
- 预先不知道函数要接受多少实参
- Python允许函数从调用语句中收集任意数量的实参
- *arg
任务: 制作披萨需要接受很多配料,但无法预先确定顾客要多少种配料
def make_pizza(*toppings):
"打印顾客所有配料"
print(toppings)
make_pizza('pepperoni')
make_pizza('pepperoni','mushrooms','extra cheese')
('pepperoni',)
('pepperoni', 'mushrooms', 'extra cheese')
8.5.1 结合使用位置实参和任意数量实参
- 要让函数接受不同类型的实参,需在函数定义中将接纳任意数量实参的形参放在最后
- Python 先匹配位置实参和关键字实参,在将余下的实参都收纳到最后一个形参中
def make_pizza(size, *toppings):
"打印顾客所有配料"
print(f"\nMaking a {size}-inch pizza with the following toppings:")
for topping in toppings:
print(f"-{topping}")
make_pizza(16,'pepperoni')
make_pizza(12,'pepperoni','mushrooms','extra cheese')
Making a 16-inch pizza with the following toppings:
-pepperoni
Making a 12-inch pizza with the following toppings:
-pepperoni
-mushrooms
-extra cheese
8.5.2 使用任意数量的关键字实参
- 需要接受任意数量的实参,但预先不知道传递给函数的是什么样的信息
- 将函数编写为能够接受任意数量的键值对——调用语句提供多少就接受多少
- **kwargs
任务: 创建用户简介,知道将接受有关用户的信息,但不确定会是什么样的信息
def build_profile(first, last, **use_info):
"创建一个字典,其中包含我们知道的有关用户的一切"
use_info['first_name'] = first
use_info['last_name'] = last
return use_info
user_profile = build_profile('albert', 'einstein',
location = 'princeton',
field = 'physics')
print(user_profile)
{'location': 'princeton', 'field': 'physics', 'first_name': 'albert', 'last_name': 'einstein'}
8.6 将函数存储在模块(Module)中
- 使用函数的好处是可以将代码块和主程序分离
- 将函数存储在称为模块(Module)的独立文件中,再将模块导入到主程序中
- 通过将函数存储在独立的文件中,可以隐藏程序代码的细节,将重点放在程序的高层逻辑上
- 模块是扩展名为.py的文件,包含要导入到程序中的代码
- import 语句允许在当前运行的程序文件中使用模块中的代码
8.6.1 导入整个模块
- import module_name
import pizza
#module_name.function_name()
pizza.make_pizza(16,'pepperoni')
pizza.make_pizza(12,'pepperoni','mushrooms','extra cheese')
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- pepperoni
- mushrooms
- extra cheese
8.6.2 导入特定函数
- from module_name import function_1, function_1
from pizza import make_pizza
make_pizza(16,'pepperoni')
make_pizza(12,'pepperoni','mushrooms','extra cheese')
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- pepperoni
- mushrooms
- extra cheese
8.6.3 使用as给函数指定别名
- from module_name import function_name as fn
from pizza import make_pizza as mp
mp(16,'pepperoni')
mp(12,'pepperoni','mushrooms','extra cheese')
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- pepperoni
- mushrooms
- extra cheese
8.6.4 使用as给模块指定别名
- import module_name as mn
import pizza as p
p.make_pizza(16,'pepperoni')
p.make_pizza(12,'pepperoni','mushrooms','extra cheese')
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- pepperoni
- mushrooms
- extra cheese
8.6.5 导入模块中所有函数
- from module_name import *
from pizza import *
make_pizza(16,'pepperoni')
make_pizza(12,'pepperoni','mushrooms','extra cheese')
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- pepperoni
- mushrooms
- extra cheese
8.7 函数编写指南
-
给函数指定描述性名称,且只在其中是用小写字母和下划线
-
每个函数都应包含简要的阐述其功能的注释,该注释应紧跟在函数定义后面,且采用文档字符串格式
-
给形参指定默认值时,等号两边不要有空格,对于函数调用中的关键字实参,也应遵循这种规定
-
如果形参过多,导致函数定义的长度超过了79字符,可在函数定义中按输入左括号后按回车键,并在下一行按两次Tab键,从而将形参列表和只缩进一层的函数体区分开来,如下
def function_name(
parameter_0,parameter_1,parameter_2,
parameter_3,parameter_4,parameter_5):
function_body...
- 如果程序或者模块包含多个函数,可使用两个空行将相邻的函数分开,这让更容易知道前一个函数在什么地方结束,下一个函数从什么地方开始
- 所有的import语句都应放在文件的开头,唯一例外的情形是,在文件的开头使用了注释来描述整个程序