Python--函数

目录

1、定义函数

1.1 向函数传递信息

1.2 实参和形参

2、传递实参

2.1 位置实参

2.2 关键字实参

2.3 默认值

2.4 等效的函数调用

2.5 避免实参错误

3、返回值

3.1 返回简单的值

3.2 让实参变成可选的

3.3 返回字典

3.4 结合使用函数和While循环

4、传递列表

4.1 在函数中修改列表

4.2 禁止函数修改列表

5、传递任意数量的实参

5.1 结合使用位置实参和任意数量的实参

5.2 使用任意数量的关键字实参

6、将函数存储在模块中

6.1 导入整个模块

6.2 导入特定的函数

6.3 使用 as 给函数指定别名

6.4 使用 as 给模块指定别名

6.5 导入模块中的所有函数

7、函数编写指南


函数是带名字的代码块, ⽤于完成具体的⼯作。要执⾏函数定义的特定任务,可调⽤(call)该 函数。

1、定义函数

下⾯是⼀个打印问候语的简单函数,名为 greet_user():

def greet_user():
 """显⽰简单的问候语"""
 print("Hello!")
greet_user()
  • 函数定义使用关键字 def
  • ⽂档字符串(docstring)的注释 通常前后分别⽤三个双引号引起,能够包含多⾏。

1.1 向函数传递信息

只需稍作修改,就可让 greet_user() 函数在问候⽤户时以其名字作为抬 头。

def greet_user(username):
 """显⽰简单的问候语"""
 print(f"Hello, {username.title()}!")
greet_user('jesse')

1.2 实参和形参

  • 在 greet_user() 函数的定义中,变量 username 是⼀个形参 (parameter),即函数完成⼯作所需的信息。
  • greet_user('jesse') 中,值 'jesse' 是⼀个实参(argument),即 在调⽤函数时传递给函数的信息。

注意:⼤家有时候会形参、实参不分。即使你看到有⼈将函数定义中 的变量称为实参或将函数调⽤中的变量称为形参,也不要⼤惊⼩怪。

2、传递实参

函数定义中可能包含多个形参,因此函数调⽤中也可能包含多个实参。

既可以使⽤位置实参,这要求实参的顺序与形 参的顺序相同;也可以使⽤关键字实参,其中每个实参都由变量名和值组 成;还可以使⽤列表和字典。

2.1 位置实参

最简单的⽅式是基于实参的顺序进⾏关联。以这种⽅式关联的 实参称为位置实参。

def describe_pet(animal_type, pet_name):
 """显⽰宠物的信息"""
 print(f"\nI have a {animal_type}.")
 print(f"My {animal_type}'s name is {pet_name.title()}.")
❷ describe_pet('hamster', 'harry')

2.2 关键字实参

关键字实参是传递给函数的名值对。这样会直接在实参中将名称和值关联 起来,因此向函数传递实参时就不会混淆了

def describe_pet(animal_type, pet_name):
 """显⽰宠物的信息"""
 print(f"\nI have a {animal_type}.")
 print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet(animal_type='hamster', pet_name='harry')

注意:在使⽤关键字实参时,务必准确地指定函数定义中的形参名。

2.3 默认值

在编写函数时,可以给每个形参指定默认值。如果在调⽤函数中给形参提 供了实参,Python 将使⽤指定的实参值;否则,将使⽤形参的默认值。

def describe_pet(pet_name, animal_type='dog'):
 """显⽰宠物的信息"""
 print(f"\nI have a {animal_type}.")
 print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet(pet_name='willie')

注意:当使⽤默认值时,必须在形参列表中先列出没有默认值的形 参,再列出有默认值的形参。这让 Python 依然能够正确地解读位置实 参。

2.4 等效的函数调用

鉴于可混合使⽤位置实参、关键字实参和默认值,通常有多种等效的函数 调⽤⽅式。

def describe_pet(pet_name, animal_type='dog'):

基于这种定义,在任何情况下都必须给 pet_name 提供实参。在指定该实 参时,既可以使⽤位置实参,也可以使⽤关键字实参。

# ⼀条名为 Willie 的⼩狗
describe_pet('willie')
describe_pet(pet_name='willie')
# ⼀只名为 Harry 的仓⿏
describe_pet('harry', 'hamster')
describe_pet(pet_name='harry', animal_type='hamster')
describe_pet(animal_type='hamster', pet_name='harry')

2.5 避免实参错误

等你开始使⽤函数后,也许会遇到实参不匹配错误。当你提供的实参多于 或少于函数完成⼯作所需的实参数量时,将出现实参不匹配错误。

def describe_pet(animal_type, pet_name):
 """显⽰宠物的信息"""
 print(f"\nI have a {animal_type}.")
 print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet()

Python 发现该函数调⽤缺少必要的信息,并⽤ traceback 指出了这⼀点:

Traceback (most recent call last):
File "pets.py", line 6, in 
describe_pet()
^^^^^^^^^^^^^^
TypeError: describe_pet() missing 2 required positional arguments: 
 'animal_type' and 'pet_name'

traceback ⾸先指出问题出在什么地⽅,让我们能够回过头去找出 函数调⽤中的错误。

然后,指出导致问题的函数调⽤。最后, traceback 指出该函数调⽤缺少两个实参,并指出了相应形参的名称。

3、返回值

函数并⾮总是直接显⽰输出,它还可以处理⼀些数据,并返回⼀个或⼀组 值。函数返回的值称为返回值。

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', 'hendrix')
 print(musician)

get_formatted_name() 函数的定义通过形参接受名和姓。它将名和姓

合在⼀起,在中间加上⼀个空格,并将结果赋给变量 full_name(⻅

❶)。然后,它将 full_name 的值转换为⾸字⺟⼤写的格式,并将结果返

回函数调⽤⾏(⻅❷)。

在调⽤可以返回值的函数时,需要提供⼀个变量,以便将返回的值赋给

它。这⾥将返回值赋给了变量 musician(⻅❸)。

3.2 让实参变成可选的

有时候,需要让实参变成可选的,以便使⽤函数的⼈只在必要时才提供额 外的信息。

假设要扩展 get_formatted_name() 函数,使其除了名和姓之外还可以

处理中间名。为此,可将其修改成类似这样:

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('john', 'lee', 'hooker')
print(musician)

然⽽,并⾮所有⼈都有中间名。为了让 get_formatted_name() 在没有提供中间名时依然正确运⾏,可给形参 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', 'hendrix')
 print(musician)
❸ musician = get_formatted_name('john', 'hooker', 'lee')
 print(musician)

3.3 返回字典

函数可返回任何类型的值,包括列表和字典等较为复杂的数据结构。例

如,下⾯的函数接受姓名的组成部分,并返回⼀个表⽰⼈的字典:

def build_person(first_name, last_name):
 """返回⼀个字典,其中包含有关⼀个⼈的信息"""
❶ person = {'first': first_name, 'last': last_name}
❷ return person
 musician = build_person('jimi', 'hendrix')
❸ print(musician)

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"\n
 Hello, {formatted_name}!")

4、传递列表

将列表传递给函数后,函数就能直接访问其 内容。下⾯使⽤函数来提⾼处理列表的效率。

def greet_users(names):
 """向列表中的每个⽤户发出简单的问候"""
 for name in names:
 msg = f"Hello, {name.title()}!"
 print(msg)
usernames = ['hannah', 'ty', 'margot']
greet_users(usernames)

4.1 在函数中修改列表

将列表传递给函数后,函数就可以对其进⾏修改了。

# ⾸先创建⼀个列表,其中包含⼀些要打印的设计
unprinted_designs = ['phone case', 'robot pendant', 'dodecahedron']
completed_models = []
# 模拟打印每个设计,直到没有未打印的设计为⽌
# 打印每个设计后,都将其移到列表 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)

可以重新组织这些代码,编写两个函数,让每个都做⼀件具体的⼯作。⼤ 部分代码与原来相同,只是结构更为合理。

def print_models(unprinted_designs, completed_models):
 """
 模拟打印每个设计,直到没有未打印的设计为⽌
 打印每个设计后,都将其移到列表 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)

4.2 禁止函数修改列表

有时候,需要禁⽌函数修改列表。为 了解决这个问题,可向函数传递列表的副本⽽不是原始列表。这样,函数 所做的任何修改都只影响副本,⽽丝毫不影响原始列表。

要将列表的副本传递给函数,可以像下⾯这样做:

function_name(list_name[:])

切⽚表⽰法 [:] 创建列表的副本。在 printing_models.py 中,如果不想清空

未打印的设计列表,可像下⾯这样调⽤ print_models():

print_models(unprinted_designs[:], completed_models)

虽然向函数传递列表的副本可保留原始列表的内容,但除⾮有充分的理 由,否则还是应该将原始列表传递给函数。这是因为,让函数使⽤现成的 列表可避免花时间和内存创建副本,从⽽提⾼效率,在处理⼤型列表时尤 其如此。

5、传递任意数量的实参

有时候,你预先不知道函数需要接受多少个实参,好在 Python 允许函数从 调⽤语句中收集任意数量的实参。

def make_pizza(*toppings):
 """打印顾客点的所有配料"""
 print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')

形参名 *toppings 中的星号让 Python 创建⼀个名为 toppings 的元组, 该元组包含函数收到的所有值。

注意,Python 会将实

参封装到⼀个元组中,即便函数只收到⼀个值也是如此:

def make_pizza(*toppings):
 """概述要制作的⽐萨"""
 print("\nMaking a pizza with the following toppings:")
 for topping in toppings:
 print(f"- {topping}")
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')

5.1 结合使用位置实参和任意数量的实参

如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实 参的形参放在最后。

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, 'mushrooms', 'green peppers', 'extra cheese')

5.2 使用任意数量的关键字实参

有时候,你需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键值对 ——调⽤语句提供了多少就接受多少。

def build_profile(first, last, **user_info):
 """创建⼀个字典,其中包含我们知道的有关⽤户的⼀切"""
❶ user_info['first_name'] = first
 user_info['last_name'] = last
 return user_info
 user_profile = build_profile('albert', 'einstein',
 location='princeton',
 field='physics')
 print(user_profile)

build_profile() 函数的定义要求提供名和姓,同时允许根据需要提供 任意数量的名值对。形参 **user_info 中的两个星号让 Python 创建⼀个 名为 user_info 的字典,该字典包含函数收到的其他所有名值对。在这 个函数中,可以像访问其他字典那样访问 user_info 中的名值对。

注意:你经常会看到形参名 **kwargs,它⽤于收集任意数量的关键 字实参。

6、将函数存储在模块中

使⽤函数的优点之⼀是可将代码块与主程序分离。通过给函数指定描述性 名称,能让程序容易理解得多。你还可以更进⼀步,将函数存储在称为模 块的独⽴⽂件中,再将模块导⼊(import)主程序。import 语句可让你在 当前运⾏的程序⽂件中使⽤模块中的代码。

6.1 导入整个模块

要让函数是可导⼊的,得先创建模块。模块是扩展名为 .py 的⽂件,包含要 导⼊程序的代码。下⾯来创建⼀个包含 make_pizza() 函数的模块。

def make_pizza(size, *toppings):
 """概述要制作的⽐萨"""
 print(f"\nMaking a {size}-inch pizza with the following toppings:")
 for topping in toppings:
 print(f"- {topping}")

接下来,在 pizza.py 所在的⽬录中创建⼀个名为 making_pizzas.py 的⽂件。 这个⽂件先导⼊刚创建的模块,再调⽤ make_pizza() 两次

import pizza
❶ pizza.make_pizza(16, 'pepperoni')
 pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

当 Python 读取这个⽂件时,代码⾏ import pizza 会让 Python 打开⽂件 pizza.py,并将其中的所有函数都复制到这个程序中。

输出结果:

Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 12-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese

这就是⼀种导⼊⽅法:只需编写⼀条 import 语句并在其中指定模块名,

就可在程序中使⽤该模块中的所有函数。如果使⽤这种 import 语句导⼊

了名为 module_name.py 的整个模块,就可使⽤下⾯的语法来使⽤其中的任

意⼀个函数:

module_name.function_name()

6.2 导入特定的函数

还可以只导⼊模块中的特定函数,语法如下:

from module_name import function_name

⽤逗号分隔函数名,可根据需要从模块中导⼊任意数量的函数:

from module_name import function_0, function_1, function_2

对于前⾯的 making_pizzas.py ⽰例,如果只想导⼊要使⽤的函数,代码将类

似于下⾯这样:

from pizza import make_pizza 
make_pizza(16, 'pepperoni') 
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

6.3 使用 as 给函数指定别名

如果要导⼊的函数的名称太⻓或者可能与程序中既有的名称冲突,可指定 简短⽽独⼀⽆⼆的别名(alias):函数的另⼀个名称,类似于外号。

from pizza import make_pizza as mp
mp(16, 'pepperoni')
mp(12, 'mushrooms', 'green peppers', 'extra cheese')

指定别名的通⽤语法如下:

from module_name import function_name as fn

6.4 使用 as 给模块指定别名

还可以给模块指定别名。通过给模块指定简短的别名(如给 pizza 模块指 定别名 p),你能够更轻松地调⽤模块中的函数。相⽐于 pizza.make_pizza(),p.make_pizza() 显然更加简洁:

import pizza as p 
p.make_pizza(16, 'pepperoni') 
p.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

给模块指定别名的通⽤语法如下:

import module_name as mn

6.5 导入模块中的所有函数

使⽤星号(*)运算符可让 Python 导⼊模块中的所有函数:

from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')

import 语句中的星号让 Python 将模块 pizza 中的每个函数都复制到这个 程序⽂件中。由于导⼊了每个函数,可通过名称来调⽤每个函数,⽆须使 ⽤点号(dot notation)。

7、函数编写指南

在编写函数时,需要牢记⼏个细节。

  • 应给函数指定描述性名称,且只使⽤ ⼩写字⺟和下划线。
  • 描述性名称可帮助你和别⼈明⽩代码想要做什么。
  • 在 给模块命名时也应遵循上述约定。
  • 每个函数都应包含简要阐述其功能的注释。该注释应紧跟在函数定义后 ⾯,并采⽤⽂档字符串的格式。

在给形参指定默认值时,等号两边不要有空格:

def function_name(parameter_0, parameter_1='default value') 

函数调⽤中的关键字实参也应遵循这种约定:

function_name(value_0, parameter_1='value')

PEP 8 建议代码⾏的⻓度不要超过 79 个字符。这样,只要编辑器窗⼝适 中,就能看到整⾏代码。

如果形参很多,导致函数定义的⻓度超过了 79 个 字符,可在函数定义中输⼊左括号后按回⻋键,并在下⼀⾏连按两次制表 符键,从⽽将形参列表和只缩进⼀层的函数体区分开来。

⼤多数编辑器会⾃动对⻬后续参数列表⾏,使其缩进程度与你给第⼀个参

数列表⾏指定的缩进程度相同:

def function_name(
     parameter_0, parameter_1, parameter_2,
     parameter_3, parameter_4, parameter_5):
     function body...

如果程序或模块包含多个函数,可使⽤两个空⾏将相邻的函数分开。这样 将更容易知道前⼀个函数到什么地⽅结束,下⼀个函数从什么地⽅开始。

所有的 import 语句都应放在⽂件开头。唯⼀的例外是,你要在⽂件开头 使⽤注释来描述整个程序。

你可能感兴趣的:(Python,python,前端,开发语言,程序人生)