第八章 函数

第八章 函数

  函数(function)是带名字的代码块,用于完成具体的工作。要执行函数定义的特定任务,可以调用函数。当需要在程序中多次执行同一项任务时,不需要反复编写完成该任务的代码,只需要调用执行该任务的函数,让Python运行其中的代码就可以。我们将会发现,使用函数,程序编写、阅读、测试和修复起来将会变得很简单。

  还将学到各种函数传递信息的方式,学习编写主要任务是显示信息的函数,以及用于处理函数并返回一个或一组值的函数。还将学习如何将函数存储在称为模块(module)的独立文件中,让主程序更加整洁。

8.1 定义函数

  Python 定义函数使用 def 关键字,一般格式如下:

def 函数名(参数列表):
    函数体

  问候语的简单函数,如下:

def greet_user():
    """定义简单的问候语"""
    print("Hello Python!")
#调用函数
greet_user()

  这就是简单的函数结构。第一行代码使用关键字def来告诉Python,我要定义一个函数。也就是函数定义,向Python指出函数名,还可以在括号内指出函数为完成任务需要什么样的信息。在这里,函数名为greet_user(),它不需要任何信息就能完成工作,因此括号内是空的(即使如此,括号也是不可少的),最重要的一点是,函数定义是以冒号结尾的。

  • 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()

  • 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。

  • 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。

  • 函数内容以冒号 : 起始,并且缩进。

  • return [表达式] 结束函数,选择性地返回一个值给调用方,不带表达式的 return 相当于返回 None。

  如下,比大小:

#比大小
def max_number(a,b):
    if a > b:
        return a
    else: 
        return b
print(f"最大的值是:{max_number(45,56)}。")

在这里插入图片描述

8.1.1 向函数传递信息

  只需要简单修改,就可让greet_user()函数在问候用户时以其名字作为抬头。因此只需要在函数定义时向括号内添加指定的变量,比如username。这样,就可让函数接受你给username指定的任何值。这就称为函数传递,如下:

def greet_user(username):
    """定义简单的问候语"""
    print(f"Hello {username.title()}!")

greet_user('alice')
#输出  Hello Alice!

  代码greet_user(‘‘alice’’)调用函数greet_user(),并向它提供执行print语句所需的信息。这个函数接受你传递给它的名字,并向这个人发出问候:

Hello Alice!

  同样,greet_user(‘alice’)调用函数greet_user()并向它传递’alice’,打印Hello,Sarah!。你可以根据需要调用函数greet_user()任意次,调用时无论传入什么样的名字,都会生成相应的输出。

8.1.2 实参和形参

  前面定义函数greet_user()时,要求给变量username指定一个值。调用这个函数并提供这种信息(人名)时,它将打印相应的问候语。

  在函数greet_user()的定义中,变量username是一个形参——函数完成其工作所需的一项信息。在代码greet_user(‘alice’)中,值’alice’是一个实参。实参是调用函数时传递给函数的信息。我们调用函数时,将要让函数使用的信息放在括号内。在greet_user(‘alice’)中,将实参’alice’传递给了函数greet_user(),这个值被存储在形参username中。

  注意: 大家有时候会形参、实参不分,因此如果你看到有人将函数定义中的变量称为实参或将函数调用中的变量称为形参,不要大惊小怪。

8.2 传递实参

  函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多:既可以使用位置实参,这要求实参的顺序与形参的顺序相同;也可以使用关键字实参,其中每个实参都由变量名和值组成;还可以使用列表和字典。

8.2.1 位置实参

  在调用函数时,Python必须将函数调用中的每个实参关联到函数定义中的一个形参,最简单的形式就是基于实参的顺序进行关联,以这种方式关联的实参称为位置实参

  举例来解释这个位置实参,如下:

def describe_pet(animal_type, pet_name):
    """显示宠物的信息"""
    print(f"我有一只{animal_type}的宠物。")
    print(f"这只{animal_type}的宠物是{pet_name}。")
print(describe_pet('漂亮的', '猫'))

  这个函数的定义表明,它需要一种动物类型和一个名字。调用describe_pet()时,需要按顺序提供一种动物类型和一个名字。**实参’漂亮的’存储在形参animal_type中,而实参’猫’**存储在形参pet_name中。在函数体内,使用了这两个形参来显示宠物的信息。

  1. 调用函数多次

    你可以根据需要调用函数任意次。要再描述一个宠物,只需再次调用describe_pet()即可:

    def describe_pet(animal_type, pet_name):
        """显示宠物的信息"""
        print(f"我有一只{animal_type}的宠物。")
        print(f"这只{animal_type}的宠物是{pet_name}。")
    print(describe_pet('漂亮的', '猫'))
    print(describe_pet('巨大的', '阿拉斯加'))
    

    第二次调用describe_pet()函数时,我们向它传递了实参’巨大的’和’阿拉斯加’。与第一次调用时一样,Python将实参’巨大的’关联到形参animal_type,并将实参’阿拉斯加’关联到形参pet_name。与前面一样,这个函数完成其任务,但打印的"我有一只巨大的阿拉斯加"。

    调用函数多次是一种效率极高的工作方式。我们只需在函数中编写描述宠物的代码一次,然后每当需要描述新宠物时,都可调用这个函数,并向它提供新宠物的信息。即便描述宠物的代码增加到了10行,你依然只需使用一行调用函数的代码,就可描述一个新宠物。在函数中,可根据需要使用任意数量的位置实参,Python将按顺序将函数调用中的实参关联到函数定义中相应的形参。

    在函数中,可根据需要使用任意数量的位置实参,Python将按顺序将函数调用中的实参关联到函数定义中相应的形参。

  2. 位置实参的顺序很重要

    在这个函数调用中,我们先指定宠物的类型,再指定宠物。由于实参’漂亮的’在前,这个值将存储到形参animal_type中;同理,'猫’将存储到形参pet_name中。结果是我们得到了“这只漂亮的的宠物是猫。”

8.2.2 关键字实参

  关键字实参是传递给函数的名值对(name-value pair)。这样会直接在实参中将名称和值关联起来,因此向函数传递实参时就不会混淆了。关键字实参不仅让你无须考虑函数调用的实参顺序,而且清楚地指出了函数调用中各个值的用途。

"""位置实参"""
def describe_pet(animal_type, pet_name):
    """显示宠物的信息"""
    print(f"我有一只{animal_type}的宠物。")
    print(f"这只{animal_type}的宠物是{pet_name}。")
print(describe_pet('漂亮的', '猫'))
print(describe_pet('巨大的', '阿拉斯加'))



help(describe_pet)


# 将实参具象化,也就是关键字实参
describe_pet(animal_type = '美丽的',pet_name = '孔雀')


# 运行结果:
我有一只美丽的的宠物。
这只美丽的的宠物是孔雀。

  describe_pet()函数,还和上面的一样,但是这次调用函数时,向Python明确地指出了各个实参对应的形参。当看到这个函数调用时,Python知道应将实参“美丽的”和“孔雀”分别赋给形参animal_type和pet_name,这样输出才能准确无误。

  关键字实参的顺序无关紧要,因为Python知道各个值该赋给哪个形参。

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

8.2.3 默认值

  在编写函数时,可以给每个形参指定默认值。如果在调用函数中给形参提供了实参,Python将使用指定的实参值;否则,将使用形参的默认值。因此,给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值不仅能简化函数调用,还能清楚地指出函数的典型用法。

  就上面的例子,我们自己将pet_name = ‘猪’,即向形参pet_name的默认值设置为“猪”,这样在进行函数调用时,就可以不用再写形参pet_name了。

def describe_pet(animal_type, pet_name = '猪'):
    """显示宠物的信息"""
    print(f"我有一只{animal_type}的宠物。")
    print(f"这只{animal_type}的宠物是{pet_name}。")
# 进行函数调用
describe_pet('非常懒')

# 运行结果:
我有一只非常懒的宠物。
这只非常懒的宠物是猪。

  这里修改了describe_pet()函数的定义,在其中给形参pet_name默认了值“猪”,这样在进行函数调用的时候就不用再给pet_name进行赋值了,Python将自动把这个实参设置为“猪”。

  请注意,在这个函数的定义中,修改了形参的排列顺序。由于给定了pet_name默认值,无须通过实参来指定pet_name是一只怎样的宠物,因此函数调用只包含一个实参,那就是pet_name是怎样的宠物。需要注意的一点是,Python已将这个实参视为位置实参,如果函数调用只包含时一只怎样的宠物,那么这个实参将被关联到函数定义中的第一个形参,也就是将animal_type放在第一个的原因。

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

8.2.4 等效的函数调用

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

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

  基于这种定义,在任何情况下都需要给animal_type提供实参。在指定该实参时,既可以使用位置实参,也可以使用关键字实参。,如果要描述的宠物不是猪,那么必须在函数调用中给pet_name提供实参。同样,在指定该实参时,既可以使用位置实参,也可以使用关键字实参。如下:

def describe_pet(animal_type, pet_name = '猪'):
    """显示宠物的信息"""
    print(f"我有一只{animal_type}的宠物。")
    print(f"这只{animal_type}的宠物是{pet_name}。\n")
# 进行函数调用
# 默认值
describe_pet('非常懒')
# 位置实参
describe_pet('巨大的','边牧')
# 关键字实参
describe_pet(animal_type = '非常的酷',pet_name = '蓝猫')

  运行结果:

我有一只非常懒的宠物。
这只非常懒的宠物是猪。


我有一只巨大的的宠物。
这只巨大的的宠物是边牧。


我有一只非常的酷的宠物。
这只非常的酷的宠物是蓝猫。

  使用那种调用方式无关紧要。可以使用对你来说最容易的理解的调用方式,只要函数调用能生成你期望的输出就好。

8.2.5 避免实参错误

  等你开始使用函数后,也许会遇到实参不匹配错误。当你提供的实参多于或少于函数完成工作所需的实参数量,将出现实参不匹配错误。如果在调用describe_pet()函数时没有指定任何实参,那么又是怎样的一种结果呢?

在这里插入图片描述

  由于没有指定位置实参、关键字实参,就导致程序报错,报错内容如下:

Traceback (most recent call last):
  File "<input>", line 14, in <module>
TypeError: describe_pet() missing 1 required positional argument: 'animal_type'

  traceback指出了问题出在什么地方,让我们能够回过头去找出函数调用中的错误。【File “”, line 14, in 】指出了导致问题的函数调用。【TypeError: describe_pet() missing 1 required positional argument: ‘animal_type’】,traceback指出该函数调用少两个实参,并指出了相应形参的名称。如果这个函数存储在一个独立的文件中,我们也许无需打开这个文件并查看函数的代码,就能重新正确地编写函数调用。

  Python读取函数的代码,并指出我们需要为哪些形参提供实参,这提供了极大的帮助。这也是应该给变量和函数指定描述性名称的另一个原因;如果你这样做了,那么无论对于你,还是可能使用你编写的代码的其他任何人来说,Python提供的错误消息都将更有帮助。

  如果提供的实参太多,将出现类似的traceback,帮助你确保函数调用和函数定义匹配。

8.3 返回值

  函数并非总是直接显示输出,它还可以处理一些数据,并返回一个或一组值。函数返回的值称为返回值。在函数中,可以使用return语句将值返回到调用函数的那行代码。返回值然你能够将程序的大部分繁重工作移到函数中完成,从而简化主程序。

8.3.1 返回简单的值

  实例如下:

def get_name(first_name,last_name):
    full_name = f"{first_name}{last_name}"
    return full_name

print(get_name('王','小二'))
# 输出结果
王小二

  在调用可以返回值的函数时,需要提供一个变量,以便将返回的值赋给它。

8.3.2 让实参变成可选的

  有时候,需要让实参变成可选的,以便使用函数的人只在必要时才提供额外的信息。可以使用默认值来让实参变成可选的。

  如果上面的例子有中间参数,那么:

def get_name(first_name,middle_name,last_name):
    full_name = f"{first_name}{middle_name}{last_name}"
    return full_name

print(get_name('王','二','麻子'))


# 运行结果:
王二麻子

  在上面的运行结果中,可以看出,如果get_name()函数,只给出两个实参,那么程序将会报错,报错如下:

在这里插入图片描述

  在Python执行缺少实参的函数时就会报错,并且报错的内容还提供了报错原因是啥。那么我们该如何解决这种问题呢

  为middle_name指定一个空的默认值(空的字符串)就行。也就是给get_name()函数,没有给定middle_name也能正常的运行。

def get_name(first_name,last_name,middle_name = ''):
    """middle_name: 相当于为True"""
    if middle_name:
        full_name = f"{first_name}{middle_name}{last_name}"
        return full_name
    else:
        full_name = f"{first_name}{last_name}"
        return full_name
print(get_name('王','小二'))
print(get_name('王','二','麻子'))



# 运行结果:
王小二
王麻子二

  在这个示例中,姓名是根据三个可能提供的部分创建的。由于人都有名和姓,因此在函数定义中首先列出了这两个形参。中间名是可选的,因此在函数定义中最后列出该形参,并将其默认值设置为空字符串。

  在函数体中,我们检查是否提供了中间名。Python将非空字符串解读为True,因此如果函数调用中提供了中间名,if middle_name将为True。如果提供了中间名,就将名、中间名和姓合并为姓名,然后返回到函数调用行。在函数调用行,将返回的值存储在变量full_name中;然后将这个变量的值打印出来。如果没有提供中间名,middle_name将为空字符串,导致if测试未通过,进而执行else代码块:只使用名和姓来生成姓名,并将设置好格式的姓名返回给函数调用行。在函数调用行,将返回的值存储在变量full_name中;然后将这个变量的值打印出来。

  可选值在让函数能够处理各种不同情形的同时,能够保证函数调用尽可能的简单。

8.3.3 返回字典

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

def getPerson_information(first_name,last_name,gender = '',age = None,career = ''):
    """返回一个字典,该字典中包含个人的基本信息"""
    person ={'first_name' : first_name,'last_name' : last_name}
    if age:
        person['age'] = age
    elif gender:
        person['gender'] = gender
    elif career:
        person['career'] = career
    return person

print(getPerson_information('王','小二','男',17))



# 输出结果如下:
{'first_name': '王', 'last_name': '小二', 'age': 17}

  在函数定义中,我们新增了可选形参gender、age以及career,并将其默认值设置为空字符串。如果函数调用中包含这个形参的值,这个值将存储到字典中。在任何情况下,这个函数都会存储人的姓名,但可对其进行修改,使其也存储有关人的其他信息。

8.3.4 结合使用函数和while循环

  尝试将函数和while循环结合使用。实例如下:

在这里插入图片描述

  程序如下:

def get_person_name(fist_name,last_name):
    full_name = f"{fist_name}{last_name}"
    return full_name
while True:
    print('请告诉我你的姓名:')
    f_name = input("first_name")
    l_name = input("last_name")
    person = get_person_name(f_name,l_name)
    print(f"你好,{person}。\n")

  执行结果:

请告诉我你的姓名:
first_name>? 黄
last_name>? 晓明
你好,黄晓明。

请告诉我你的姓名:
first_name>? 刘
last_name>? 亦菲
你好,刘亦菲。

请告诉我你的姓名:
first_name
>?【输入的地方】

  我们添加了一条消息来告诉用户如何退出,然后在每次提示用户输入时,都检查他输入的是否是退出值“q”,如果是,就退出循环。现在,这个程序将不断地问候,直到用户输入的姓或名为’q’为止

def get_person_names(fist_name,last_name):
    """返回规范的姓名"""
    full_names = f"{fist_name}{last_name}"
    return full_names

while True:
    print('请告诉我你的姓名:')
    print("请输入 'q',退出循环")
    f_name = input("first_name")
    if f_name == 'q':
        break

    l_name = input("last_name")
    if l_name == 'q':
        break
    person = get_person_names(f_name,l_name)
    print(f"你好,{person}。\n")

  运行结果:

请告诉我你的姓名:
请输入 'q',退出循环
first_name>? 小
last_name>? 王
你好,小王。

请告诉我你的姓名:
请输入 'q',退出循环
first_name>? 大
last_name>? 王
你好,大王。

请告诉我你的姓名:
请输入 'q',退出循环
first_name>? q
# 退出循环即结束

8.4 传递列表

  在写Python的时候会发现,向函数传递列表非常的有用,可能是名字列表、数值列表或更复杂的对象列表(如字典)。将列表传递给函数后,函数就能直接访问其内容。接下来我们就使用函数来提高处理列表的效率。

  定义一个用户列表,然后使用函数来访问这个列表并且打出问候语。

def Hello_Persom(names):
    for i in names:
        print(f"你好,{i},很高兴见到你。")

# 定义一个列表,然后调用函数
names = ['小王','老王','孙少平','孙少安','老李头','刘二麻子','小赵']
Hello_Persom(names)

  在上面的程序中,我将Hello_Person()定义成一个可以接受列表的函数名,并将赋给形参names。然后这个函数遍历接收到的列表,并且对每个用户打印一条问候语。在函数外,需要定义一个列表names,然后再调用Hello_Person()并将这个列表传递给它。

  运行结果:

你好,小王,很高兴见到你。
你好,老王,很高兴见到你。
你好,孙少平,很高兴见到你。
你好,孙少安,很高兴见到你。
你好,老李头,很高兴见到你。

  由运行结果可知,输出完全在预料之中。

8.4.1 在函数中修改列表

  将列表传递给函数后,函数就可以对其进行修改了。在函数中对这个列表所做的任何修改都是永久的,这让你能够高效地处理大量数据。

# 在函数中修改列表
# 函数形参,传递两个列表,按位置形参,一前一后
"""
1、编写两个函数,一个传递两个形参,即函数Business_Design(),在函数体内,使用了while循环,遍历列表names,并且每循环一次就将列表中的元素追加到列表person中。
2、然后编写第二个函数,即print_Person(),将上一个函数得到列表使用for循环遍历并打印出来。
"""
def Business_Design(names,Person):
    """定义的这个函数,是将前后的形参值进行互换"""
    while names:
        i = names.pop()
        print(f"你好,{i},今天的大冒险玩的开心吗?。")
        Person.append(i)

def print_Person(names):
    """打印出上个函数传递的值"""
    for i in names:
        print(f"很高兴,{i}来到了这里,真心为你感到高兴。")

names = ['秦始皇','武则天','刘亦菲','范冰冰','李连杰','刘浩存','刘昊然','王宝强','古力娜扎']
person = []
Business_Design(names,person)
print_Person(person)




# 等价的写法
"""
两个for循环就能实现传递
"""
names = ['秦始皇','武则天','刘亦菲','范冰冰','李连杰','刘浩存','刘昊然','王宝强','古力娜扎']
person = []
for i in names:
    # i = names.pop()
    print(f"你好,{i},今天的大冒险玩的开心吗?")
    person.append(i)
print('\n')   
for a in person:
    print(f"很高兴,{a}来到了这里,真心为你感到高兴。")

  运行结果:

# 在函数中修改列表
# 函数形参,传递两个列表,按位置形参,一前一后
你好,秦始皇,今天的大冒险玩的开心吗?
你好,武则天,今天的大冒险玩的开心吗?
你好,刘亦菲,今天的大冒险玩的开心吗?
你好,范冰冰,今天的大冒险玩的开心吗?
你好,李连杰,今天的大冒险玩的开心吗?
你好,刘浩存,今天的大冒险玩的开心吗?
你好,刘昊然,今天的大冒险玩的开心吗?
你好,王宝强,今天的大冒险玩的开心吗?
你好,古力娜扎,今天的大冒险玩的开心吗?



# 等价的写法
很高兴,秦始皇来到了这里,真心为你感到高兴。
很高兴,武则天来到了这里,真心为你感到高兴。
很高兴,刘亦菲来到了这里,真心为你感到高兴。
很高兴,范冰冰来到了这里,真心为你感到高兴。
很高兴,李连杰来到了这里,真心为你感到高兴。
很高兴,刘浩存来到了这里,真心为你感到高兴。
很高兴,刘昊然来到了这里,真心为你感到高兴。
很高兴,王宝强来到了这里,真心为你感到高兴。
很高兴,古力娜扎来到了这里,真心为你感到高兴。

  这个函数传递的程序还演示了这样一种理念,即每个函数都应只负责一项具体的工作。第一个函数打印每个用户并且将其传递给另外一个列表,而第二个是打印第一个函数传递的列表;这优于使用一个函数来完成两项工作。编写函数时,如果你发现它执行的任务太多,请尝试将这些代码划分到两个函数中。别忘了,总是可以在一个函数中调用另一个函数,这有助于将复杂的任务划分成一系列的步骤。

8.4.2 禁止函数修改列表

  我们可以复制列表,即使用切片器,也就是同时省略起始索引和终止索引([:])。让Python创建一个起始于第一个元素、终止于最后一个元素的切片,即复制整个列表。

		"""使用切片器进行复制"""
        copy_name = names[:]

在这里插入图片描述

  程序,使用切片器进行复制:

def Business_Design(names,Person):
    """定义的这个函数,是将前后的形参值进行互换"""
    while names:
        """使用切片器进行复制"""
        copy_name = names[:]
        i = names.pop()
        print(f"你好,{i},今天的大冒险玩的开心吗?。")
        Person.append(i)

def print_Person(names):
    """打印出上个函数传递的值"""
    for i in names:
        print(f"很高兴,{i}来到了这里,真心为你感到高兴。")

names = ['秦始皇','武则天','刘亦菲','范冰冰','李连杰','刘浩存','刘昊然','王宝强','古力娜扎']
person = []
Business_Design(names,person)
print_Person(person)

8.5 传递任意数量的实参

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

  简单来说,就是返回的结果形成的是一组《元组》。

"""传递任意数量的实参
1、返回的是一组《元组》
"""
def name(*args):
    """以一个星号打头,后面跟一个形参
    也就是无论向函数扔进多少个实参,最终都会以《元组》的形式呈现出来
    """
    print(args)
    
name('小明')
name('雨儿','瑕疵','明天','未来')


# 输出结果
('小明',)
('雨儿', '瑕疵', '明天', '未来')

  比较复杂的写法:

def name(*args):
    """以一个星号打头,后面跟一个形参
    也就是无论向函数扔进多少个实参,最终都会以《元组》的形式呈现出来
    """
    """得到这个实参"""
    print(args)
    """遍历这个实参"""
    for i in args:
        print(f"{i}一定会更好!!!")
    print("下个路口再见!!\n")

name('小明')
name('雨儿','瑕疵','明天','未来')


# 运行结果
('小明',)
小明一定会更好!!!
下个路口再见!!


('雨儿', '瑕疵', '明天', '未来')
雨儿一定会更好!!!
瑕疵一定会更好!!!
明天一定会更好!!!
未来一定会更好!!!
下个路口再见!!

  也就是无论向函数扔进多少个实参,最终都会以《元组》的形式呈现出来。

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

  如果要让函数接受不同类型的实参,必须在函数定义中将接受任意数量实参的的形参放在最后。Python将优先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。

"""位置实参和任意数量的实参结合使用"""
def names(age,*args):
    """位置实参和任意数量的实参结合使用"""
    print(f"{age}岁,即同岁的学生有:")
    for i in args:
        print('  ' + i)
    print("大家真优秀!!!")
    

names(23,'雨儿','瑕疵','明天','未来','生活')

  运行结果:

23岁,即同岁的学生有:
  雨儿
  瑕疵
  明天
  未来

  注意:我们在程序中,会经常遇到用形参命名的《*args》,它的主要作用是收集任意数量的位置实参。

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

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

def useName(first, last, **kwargs):
    """创建一个字典,包含我们需要的信息"""
    kwargs['firstname'] = first
    kwargs['lastname'] = last
    return kwargs


"""输出这个任意位置的关键字实参"""
useFamily = useName(
    '雨儿', '瑕疵',
    location='昭通镇雄',
    area='中国'
)
print(useFamily)


# 输出结果
{'location': '昭通镇雄', 'area': '中国', 'firstname': '雨儿', 'lastname': '瑕疵'}

  在编写函数时,可以使用各种方式混合使用位置实参、关键字实参和任意数量的实参。知道这些实参类型非常的重要,我们需得正确的使用这些类型的实参,需要刻意的去练习。

**注意:**我们会经常遇到《**kwargs》的形参,它主要用于收集任意数量的关键字实参。

8.6 将函数存储到模块中

  使用函数的优点之一是可将代码块与主程序分离。通过给函数指定描述性的名称,能够让程序容易理解的多。还可以将函数存储在称为模块的独立文件中,再将模块导入(import)主程序。import语句可让你在当前运行的程序文件中使用模块中的文件。

  通过将函数存储在独立的文件中,可隐藏程序代码的细节,将重点放在程序的高层逻辑上,这也能使我们在不同的程序中复用函数。将函数存储在独立的文件中后,可与其他程序员共享这些文件而不是整个程序。并且知道如何导入函数,还能让你使用其他程序员编写的函数库。

8.6.1 导入整个模块

  要让函数是可导入的,需要先创建模块。模块的拓展名就是.py的文件,包含要导入程序的代码。

  创建一个名为【importFunction.py】的文件

# 创建一个名为【importFunction.py】的文件
def importFunction(names):
    """打印出上个函数传递的值"""
    for i in names:
        print(f"很高兴,{i}来到了这里,真心为你感到高兴。")

  再创建一个名为【callFunction.py】的文件,并在这个文件中导入上个文件写的函数。

# 导入模块包
import importFunction as f
names = ['秦始皇', '武则天', '刘亦菲', '范冰冰', '李连杰', '刘浩存', '刘昊然', '王宝强', '古力娜扎']
#调用模块包中的函数
f.importFunction(names)

  在名为【callFunction.py】的文件中运行结果:

很高兴,秦始皇来到了这里,真心为你感到高兴。
很高兴,武则天来到了这里,真心为你感到高兴。
很高兴,刘亦菲来到了这里,真心为你感到高兴。
很高兴,范冰冰来到了这里,真心为你感到高兴。
很高兴,李连杰来到了这里,真心为你感到高兴。
很高兴,刘浩存来到了这里,真心为你感到高兴。
很高兴,刘昊然来到了这里,真心为你感到高兴。
很高兴,王宝强来到了这里,真心为你感到高兴。
很高兴,古力娜扎来到了这里,真心为你感到高兴。

  当Python读取这个文件时,代码行【importFunction.py】会让Python打开这个文件【importFunction.py】,并将其中的所有函数都复制到这个程序中,但是你看不到复制程序的过程,因为这个复制都是在运行中自动将程序复制到本程序中,我们可以使用【importFunction.py】中定义的所有函数。

  这是一种导入的方法,只需要编写一条import语句并在其中指定模块名,就可以在程序中使用该模块中的所有函数。

8.6.2 导入特定的函数

  还可以导入模块中特定的函数。语法如下:

from function import getPerson_information

  这句语法的意思是,从文件function中,导入模块包【getPerson_information】;

  模块包的写法:

def getPerson_information(first_name, last_name, gender='', age=None, career=''):
    person = {'first_name': first_name, 'last_name': last_name}
    if age:
        person['age'] = age
    elif gender:
        person['gender'] = gender
    elif career:
        person['career'] = career
    return person

  调用的写法:

from function import getPerson_information
names = getPerson_information('王', '小二', '男', 17)
print(names)

  打印结果:

# 得到一个字典
{'first_name': '王', 'last_name': '小二', 'age': 17}
8.6.3 使用as给函数指定别名

  如果要导入的函数名称太长或者可能与程序既有的名称冲突,可指定简短而独一无二的别名(alias),函数的另外一个名称,类似于人的外号。

  语法:

from function import getPerson_information as gi

  具体的用法:

from function import getPerson_information as gi
names = gi('王', '小二', '男', 17)
print(names)

  输出结果:

{'first_name': '王', 'last_name': '小二', 'age': 17}

  上面的语句,给【getPerson_information】起了一个别名,叫gi,即使用gi()就可以进行函数的调用。

  通用的语法:

from module_name import function_name as gi

# 解释
module_name  是创建的文件名,.py的文件名
function_name   是创建的函数名
8.6.4 使用as给模块指定别名

  还可以给模块指定别名。通过给模块指定简短的别名,便能轻松的调用模块中的函数。

  模块包:

def printNumber(a,b):
    """打印指定范围内的乘法表"""
    for i in range(a, b):
        for j in range(1, i + 1):
            print(f'{j}x{i}={i*j}\t', end='')
        print()


def printName(name):
    """打印名字"""
    for i in name:
        print(f"您好,'{i}'\t", end='\n')

  调用这个模块包:

import importFunction as iF

print(iF.printNumber(5,13))

name = ['秦始皇', '武则天', '刘亦菲', '范冰冰', '李连杰', '刘浩存', '刘昊然', '王宝强', '古力娜扎']
iF.printName(name)

  输出结果:

"""打印指定范围内的乘法表"""
1x5=5	2x5=10	3x5=15	4x5=20	5x5=25	
1x6=6	2x6=12	3x6=18	4x6=24	5x6=30	6x6=36	
1x7=7	2x7=14	3x7=21	4x7=28	5x7=35	6x7=42	7x7=49	
1x8=8	2x8=16	3x8=24	4x8=32	5x8=40	6x8=48	7x8=56	8x8=64	
1x9=9	2x9=18	3x9=27	4x9=36	5x9=45	6x9=54	7x9=63	8x9=72	9x9=81	
1x10=10	2x10=20	3x10=30	4x10=40	5x10=50	6x10=60	7x10=70	8x10=80	9x10=90	10x10=100	
1x11=11	2x11=22	3x11=33	4x11=44	5x11=55	6x11=66	7x11=77	8x11=88	9x11=99	10x11=110	11x11=121	
1x12=12	2x12=24	3x12=36	4x12=48	5x12=60	6x12=72	7x12=84	8x12=96	9x12=108 10x12=120	11x12=132 12x12=144	



"""打印名字"""
您好,'秦始皇'	
您好,'武则天'	
您好,'刘亦菲'	
您好,'范冰冰'	
您好,'李连杰'	
您好,'刘浩存'	
您好,'刘昊然'	
您好,'王宝强'	
您好,'古力娜扎'	

  **解释:**这个是将模块【importFunction.py】专门用来书写函数,然后再使用import来直接导入的新Python文件中,在新文件中直接调用。这样可以看出,代码更加简洁,只用专注于描述性的函数名。这些函数名明确的指出了函数的功能,对于理解代码来说,它们比模块名更重要。

  通用的语法:

import module_name as mn
8.6.5 导入模块中的所有函数

  使用星号(*)运算符可让Python导入模块中的所有函数。

# 使用星号(*)运算符可让Python导入模块中的所有函数
from importFunction import *

print(printNumber(5,13))

name = ['秦始皇', '武则天', '刘亦菲', '范冰冰', '李连杰', '刘浩存', '刘昊然', '王宝强', '古力娜扎']
printName(name)

  import语句中的星号让Python将模块importFunction中的每个函数都复制到这个程序文件中。由于导入了每个函数,可通过名称来调用每个函数,而无需使用**点号(dot notation)**表示法。然而,使用并非自己编写的大型模块时,最好不要采用这种导入方法:如果模块中有函数的名称与你的项目中使用的名称相同,可能导致意想不到的结果:Python可能遇到多个名称相同的函数或变量,进而覆盖函数,而不是分别导入所有的函数。

  最佳的做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法。这能让代码更清晰,更容易阅读和理解。这里之所以介绍这种导入方法,只是想让你在阅读别人编写的代码时,如果遇到类似于下面的import语句,能够理解它们:

from module_name import *

8.7 函数编写指南

  编写函数时,需要牢记几个细节:应给函数指定描述性名称,且只在其中使用小写字母和下划线。描述性名称可帮助你和别人明白代码想要做什么。给模块命名时也应遵循上述约定。

  每个函数都应包含简要地阐述其功能的注释,该注释应紧跟在函数定义后面,并采用文档字符串格式。文档良好的函数让其他程序员只需阅读文档字符串中的描述就能够使用它:他们完全可以相信代码如描述的那样运行;只要知道函数的名称、需要的实参以及返回值的类型,就能在自己的程序中使用它。

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

  对于函数调用中的关键字实参,也应遵循这种约定:

  PEP 8(https://www.python.org/dev/peps/pep-0008/)建议代码行的长度不要超过79字符,这样只要编辑器窗口适中,就能看到整行代码。如果形参很多,导致函数定义的长度超过了79字符,可在函数定义中输入左括号后按回车键,并在下一行按两次Tab键,从而将形参列表和只缩进一层的函数体区分开来。

  大多数编辑器都会自动对齐后续参数列表行,使其缩进程度与你给第一个参数列表行指定的缩进程度相同:

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

  如果程序或模块包含多个函数,可使用两个空行将相邻的函数分开,这样将更容易知道前一个函数在什么地方结束,下一个函数从什么地方开始。所有的import语句都应放在文件开头,唯一例外的情形是,在文件开头使用了注释来描述整个程序。

8.8 小结

本章学习了:

  1. 如何编写函数,以及如何传递实参,让函数能够访问完成其工作所需的信息;
  2. 如何使用位置实参和关键字实参,以及如何接受任意数量的实参;
  3. 显示输出的函数和返回值的函数;
  4. 如何将函数同列表、字典、if语句和while循环结合起来使用。
  5. 你还知道了如何将函数存储在被称为模块的独立文件中,让程序文件更简单、更易于理解。
  6. 最后,你学习了函数编写指南,遵循这些指南可让程序始终结构良好,并对你和其他人来说易于阅读。
    • 程序员的目标之一是,编写简单的代码来完成任务,而函数有助于你实现这样的目标。
    • 它们让你编写好代码块并确定其能够正确运行后,就可置之不理。
    • 确定函数能够正确地完成其工作后,你就可以接着投身于下一个编码任务。
    • 函数让你编写代码一次后,想重用它们多少次就重用多少次。
    • 需要运行函数中的代码时,只需编写一行函数调用代码,就可让函数完成其工作。
    • 需要修改函数的行为时,只需修改一个代码块,而所做的修改将影响调用这个函数的每个地方。
    • 使用函数让程序更容易阅读,而良好的函数名概述了程序各个部分的作用。
    • 相对于阅读一系列的代码块,阅读一系列函数调用让你能够更快地明白程序的作用。
  7. 函数还让代码更容易测试和调试。如果程序使用一系列的函数来完成其任务,而其中的每个函数都完成一项具体的工作,测试和维护起来将容易得多:你可编写分别调用每个函数的程序,并测试每个函数是否在它可能遇到的各种情形下都能正确地运行。经过这样的测试后你就能信心满满,深信你每次调用这些函数时,它们都将正确地运行。

  以上的内容,是本人自学Python,写的笔记,如有错误,请留言哦!!!本人会在第一时间及时更正。如果你觉得有用,请关注我,一起学习Python,一起高薪!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

殇淋狱陌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值