Python之路,Day04-函数与模块

 

本节内容

1、函数的定义

2、如何使用函数

3、函数的返回值

4、使用注释改进文档

5、传递任意数量的实参

6、函数与模块

 

 

 

1、函数的定义

函数的一般定义(中学/数学):函数的近代定义是给定一个数集A,假设其中的元素为x,对A中的元素x施加对应法则f,记作f(x),得到另一数集B,假设B中的元素为y,则y与x之间的等量关系可以用y=f(x)表示,函数概念含有三个要素:定义域A、值域C和对应法则f。

 

编程语言中函数的定义(计算机):函数是逻辑结构化和过程化的一种编程方法。

函数的定义方法(案例):

>>> def fib(n):    # write Fibonacci series up to n
...     """Print a Fibonacci series up to n."""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b, a+b
...     print()

"""
def:定义函数的关键字
flb:函数名
(n):定义函数的参数
"""""":文档描述
代码块:
     a, b = 0, 1
     while a < n:
        print(a, end=' ')
         a, b = b, a+b
     print()
"""

 具体学习:

下面打印一个问候的简单函数:

1 def greet_user():    #定义一个函数,关键词为"def",函数名为"greet_user",最后以“:”结尾
2     """显示简单的问候语"""    #描述函数的具体功能
3     print("Hello!")         #函数体的代码块,用于实现函数功能
4 
5 greet_user()         #调用函数

 注意:(1)在第一行定义函数中,不需要任何信息就能完成工作,因此括号是空的(即便如此,括号必不可少!)

   (2)要调用函数,可以依次指定函数名以及括号括起的必要信息。在第五行代码中,因为此函数greet_uesr()括号中不需要任何信息,只需要输入greet_uesr()即可。和预期效果一样,打印Hello!:

练习:大脑P149

1 def search4vowels():
2     vowels = set('aeiou')
3     word = input("Provide a word to search for vowels:")
4     found = vowels.intersection(set(word))
5     for vowel in found:
6         print(vowel)
7 
8 search4vowels()
9 search4vowels() #可以重复多次调用
search_vowels

2、如何使用函数(传递实参)

函数的使用方法(先看案例)

>>> def fib2(n):  # return Fibonacci series up to n
...     """Return a list containing the Fibonacci series up to n."""
...     result = []
...     a, b = 0, 1
...     while a < n:
...         result.append(a)    # see below
...         a, b = b, a+b
...     return result
...
>>> f100 = fib2(100)    # call it
>>> f100                # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

 2.1 向函数传递信息

针对greet_user()只要稍作修改,就可不仅实现Hello,还可以将用户名字作为抬头:

1 def greet_user(username):    
2     """显示简单的问候语"""    
3     print("hello,"+username.title()+"!")         
4 
5 greet_user("zhichao")  

代码greet_user("zhichao")调用函数greet_user()时,向它传递执行print()语句所需要的信息username。

2.2 实参和形参

greet_user(username) #username 为形参;函数完成其工作时所需要的信息。

greet_user("zhichao") #"zhichao"为实参;实参是调用函数时传递给函数的信息。

形参:形式参数,不是实际存在的,是虚拟变量。在定义函数和函数体的时候使用形参,目的是在函数调用时接收实参

实参:实际参数,调用函数时传递给函数的参数,可以是常量、变量,表达式,函数,传给形参

区别:形参是虚拟的,不占用空间,形参变量只有在被调用时才分配内存单元,实参是一个变量,占用内存空间,数据传递单向,实参传给形参,不能倒过来。

传递实参:

1、位置实参(基于实参的顺序):

1 #zhichao
2 
3 def describe_pet(animal_type,pet_name):
4     """显示宠物信息"""
5     print("\nI have a" + animal_type + ".")
6     print("My" + animal_type + "'s name is "+pet_name.title() + ".")
7 
8 describe_pet('hamster','harry')
9 describe_pet('cat','python')  #函数调用多次是一种效率极高的工作方式

位置实参的顺序很重要,请确认函数调用中实参的顺序与函数定义形参的顺序一致

2 关键字参数

1 def describe_pet(pet_name,animal_type="dog",):
2     """显示宠物信息"""
3     print("\nI have a " + animal_type + ".")
4     print("My" + animal_type + "'s name is "+pet_name.title() + ".")
5 
6 describe_pet(pet_name='harry',animal_type='hamster')  #关键字实参
7 describe_pet(pet_name='python',animal_type='cat')  #关键字实参

思考1:关键字参数是否需要与形参顺序一致?

思考2:关键字参数和位置参数能否同时存在?

例:

#eg1
def func_test(x,y):
    print(x)
    print(y)
    
func_test(x=1,2)      #

#eg2
def func_test(x,y):
    print(x)
    print(y)
    
func_test(1,y=2)      #

#eg3
def func_test(x,y,z):
    print(x)
    print(y)
    
func_test(1,y=2,3)      #

#结论?

****关键参数是不可以写在位置参数前面的

2.3 默认值

编写函数时,可以给形参指定默认值。

1 def describe_pet(pet_name,animal_type="dog",):
2     """显示宠物信息"""
3     print("\nI have a " + animal_type + ".")
4     print("My" + animal_type + "'s name is "+pet_name.title() + ".")
5 
6 describe_pet('harry','hamster')
7 describe_pet('python','cat')

 2.4 像函数中传递列表:

eg:

 1 #Zhichao
 2 
 3 def greet_users(names):
 4     """向列表中的每位用户都发出简单的问候"""
 5     for name in names:
 6         msg = "Hello, "+name.title() +"!"
 7         print(msg)
 8 
 9 usernames = ['hannah','ty','margot']
10 greet_users(usernames)
greet_users

 

3、函数的返回值

 函数返回的值被称为函数的返回值;

在函数中,可用return语句将值返回到调用函数的代码行;

返回值能将你程序的大部分繁重工作移到函数中去完成,从而简化主程序。

3.1 返回简单值

eg(formatted_name1):

1 #zhichao
2 
3 def get_formatted_name(first_name,last_name):
4     """返回整洁的姓名"""
5     full_name = first_name + ' '+ last_name
6     return full_name.title()
7 
8 musician = get_formatted_name('jimi','hendrix') #调用返回值的函数时,需要提供一个变量,用于存储返回的值
9 print(musician)

 

3.2 让实参变成可选的

有时候,需要让实参变成可选的,这样使用函数的人就只需要在必要时候才提供额外信息。

我们对:formatted_name1 进行扩展(函数的可扩展性

eg(formatted_name2):

1 def get_formatted_name(first_name,middle_name,last_name):
2     """返回整洁的姓名"""
3     full_name = first_name + ' '+middle_name+' '+ last_name
4     return full_name.title()
5 
6 musician = get_formatted_name('jimi','lee','hendrix') #有些人有中间名
7 print(musician)

有些人不一定有中间名,那么对函数进行优化:

eg(formatted_name2):

#zhichao

def get_formatted_name(first_name,last_name,middle_name=''):  #复习关键字实参放后面
    """返回整洁的姓名"""
    if middle_name:
        full_name = first_name + ' '+middle_name+' '+ last_name
    else:
        full_name = first_name + ' ' + last_name
    return full_name.title()

musician = get_formatted_name('jimi','lee','hendrix')
musician1 = get_formatted_name('jimi','hendrix')
print(musician)
print(musician1)

 

3.3 返回多个值

函数可以返回任意类型的值,包括列表、字典和集合等较为复杂的数据结构。

大脑P159:

返回一个值:

return_bool

返回一个集合:

1 # Zhichao
2 
3 def search4vowels(word):
4     """Return a boolean based on any vowels found"""
5     vowels = set('aeiou')
6     found = vowels.intersection(set(word))
7     return found
8 word = search4vowels('hello Zhichao')
9 print(word)
return_set

返回数据:

1 #Zhichao
2 
3 def search4vowels(word):
4     """Return a boolean based on any vowels found"""
5     vowels = set('aeiou')
6     return vowels.intersection(set(word))
7 word = search4vowels('hello Zhichao')
8 print(word)
return_value

返回字典:

1 #Zhichao
2 
3 def build_person(first_name,last_name):
4     """返回一个字典,其中包含有关一个人的信息"""
5     person = {'first':first_name,'last':last_name}
6     return person
7 
8 musician = build_person('jimi','hendrix')
9 print(musician)
return_dict

 

4.使用注释改进文档

复习python的四种数据类型。

当我们调用函数时,才知道我们需要输入的参数和返回值的类型“type”

对此,我们的一种解决办法是把这个信息增加到docstring

python3 注解的记法

1.函数注解是可选的。

2.函数注解可以提供信息。

 

eg:

1 #zhichao
2 
3 def search4vowels(word:str) ->set:
4     """Return a boolean based on any vowels found"""
5     vowels = set('aeiou')
6     return vowels.intersection(set(word))
7 
8 help(search4vowels)

 

 5.传递任意数量的实参

1. *args,传入多个参数,转化成元组。

假如一个函数定义一个披萨的配料,但并不知道有多少配料需要加入,在参数不确定的情况下,我们引入任意数量的实参。

eg:

#Zhichao

def make_pizza(*toppings):  
    """打印顾客点的所有配料"""
    print(toopings)

make_pizza("peperoni")
make_pizza("mushroom","green peppers","extra cheese")

注意:*toppings的值是一个封装好的空元组,并将所有接收到的值都封装在这个元组里。

 

2.**kwargs,把关键字参数,转化成字典。

eg:

1 # -*- coding:utf-8 -*-
2 # Author:Zhichao
3 
4 def infos(**kwargs):
5     """打印个人信息"""
6     print(kwargs)
7 
8 infos(name="Zhichao",age="24",job="IT")

 

 

 

 设计一个对顾客点披萨进行描述:

 1 #Zhichao
 2 
 3 def make_pizza(*toopings):
 4     """概述要制作的披萨"""
 5     print("\nMaking a pizza with the following toppings")
 6     for topping in toopings:
 7         print("--"+topping)
 8 
 9 make_pizza("peperoni")
10 make_pizza("mushroom","green peppers","extra cheese")

 

结合位置实参和任意数量实参:

#Zhichao

def make_pizza(size,*toopings):  #回顾位置实参应放在前面
    """概述要制作的披萨"""
    print("\nMaking a "+str(size)+"-inch pizza with the following toppings")
    for topping in toopings:
        print("--"+topping)

make_pizza(14,"peperoni")
make_pizza(12,"mushroom","green peppers","extra cheese")

 

6.函数与模块

函数的优点之一是将代码与主程序分离;

我们可以更进一步,将函数存储在被称为“模块”的独立文件中,再将模块导入主程序;

import语句允许我们在当前运行的程序文件中使用模块中的代码。

优势:

1.通过将函数存储在独立的文件中,可隐藏程序代码的细节,将重点放在程序的高层逻辑上;

2.可以让不同的程序中重用函数;

3.可与其他程序员共享这些文件而不是整个程序;

4.知道如何导入函数能让你使用其他程序员编写的函数库。

 

6.1 导入整个模块

模块是扩展名为.py的文件(如一些内置的模块:C:\ProgramData\Anaconda3\Lib)

下面我们来创建一个模包含函数make_pizza()的模块。

pizza.py

1 #Zhichao
2 
3 def make_pizza(size,*toopings):
4     """概述要制作的披萨"""
5     print("\nMaking a "+str(size)+"-inch pizza with the following toppings")
6     for topping in toopings:
7         print("--"+topping)

 

接下来,我们在pizza.py所在目录下创建一个另外的名为making_pizzas.py文件,这个文件导入刚创建的模块,在调用make_pizza()两次:

making_pizzas.py

1 import pizza #调用模块
2 
3 pizza.make_pizza(16,'pepperoni')  #调用模块的函数
4 pizza.make_pizza(12,"mushroom","green peppers","extra cheese")

 

 

 

 6.2 导入特定的函数

可以导入模块中的特定函数,方法如下:

1 from module_name import function_name

通过用逗号分开函数名可以同时导入多个函数:

1 from module_name import function_0,function_1,function_2

对于前面的pizza案例,我们可以导入模块中的特定函数:

1 from pizza import make_pizza
2 make_pizza(16,'pepperoni')  #调用模块的函数
3 make_pizza(12,"mushroom","green peppers","extra cheese")

若使用此种方法,调用函数时不需要用句点。

 

6.3 使用as 给函数指定别名

思考:如果你导入的函数名与程序中现有的名称冲突怎么办?或者你调用的函数名称太长怎么办?

 

我们可以指定函数的另一个名称,类似于外号。

下面我们根据make_pizza指定名称

1 from pizza import make_pizza as mp
2 mp(16,'pepperoni')  #调用模块的函数
3 mp(12,"mushroom","green peppers","extra cheese")

通用语法格式:

1 from moudle_name import function_name as fn

 

6.4 使用as给模块指定别名

eg:

1 import pizza as p
2 
3 p.make_pizza(16,'pepperoni')  
4 p.make_pizza(12,"mushroom","green peppers","extra cheese")

通用语法格式:

import moudle_name as mn

 

6.5 导入模块中的所有函数

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

1 #Zhichao
2 from pizza import*
3 
4 make_pizza(16,'pepperoni')
5 make_pizza(12,"mushroom","green peppers","extra cheese")

通用语法格式:

from moudle_name import *

 

Head First Python

大脑P162

使用注解改进文档:

 

关于注解更多详细内容参见PEP3107

(https://www.python.org/dev/peps/pep-3107)

大脑 P164

函数:我们已经知道些什么

* 函数是命名的代码块。

* def关键字用来命名函数,函数代码在def关键字下(相对于def关键字)缩进

* Python的三重引号字符串可以用来函数增加多行注释。如果采用这种方式,他们称之为docstring

* 函数可以接受任意多个命名参数

* return语句允许函数返回任意值(也可以不反回任何值)

*函数注解可以用来描述函数参数的类型

 

建立一个通用的函数:

 1 #Zhichao
 2 
 3 def search4vowels(phrase: str) -> set:
 4     """return any vowels found in a supplied phrase"""
 5     vowels = set('aeiou')
 6     return vowels.intersection(set(phrase))
 7 
 8 def search4letters(phrase:str,letters:str)->set:
 9     """return a set of the 'letters' found in 'phrase'. """
10     return set(letters).intersection(set(phrase))
11 
12 help(search4letters)
13 print(search4letters('hitch-hiker','aeiou'))
14 print(search4letters('galaxy','xyz'))

 

 

函数补充:

函数可以调用函数吗?

函数引用

eg:

 1 # Author:Zhixhao
 2 
 3 def search4letters(phrase:str,letters:str)->set:
 4     """return a set of the 'letters' found in 'phrase'. """
 5     logger("search4letters")
 6     return set(letters).intersection(set(phrase))
 7 
 8 def logger(source):
 9     print("from %s"%source)
10 help(search4letters)
11 
12 print(search4letters('hitch-hiker','aeiou'))
13 print(search4letters('galaxy','xyz'))

 

局部变量与全局变量以及其作用域:

 1 #Zhichao
 2 
 3 school = "中山大学"
 4 
 5 def change_name(name:str):
 6     """修改名字"""
 7     # global school
 8     school = "中山大学南方学院"
 9     print("before change",name,school)
10     name = name.title()         #局部变量,可以理解这个函数就是这个变量的作用域
11     # age = 18
12     print("after change",name)
13 
14 # print("age",age)
15 name = "zhichao"
16 change_name(name)
17 print(name,school)

 

不建议使用  global 在函数内部修改全局变量,容易导致逻辑混乱不清

再来看一个例子:

1 # Author:Zhichao
2 
3 names = ["Marry","Jack","Lin"]
4 def change_name():
5     names.append("Zhichao")
6     print("inside func",names)
7 
8 change_name()
9 print(names)

请阅读大脑P185-p187

并尝试理解:

 

 

总结:

可变:列表、字典和集合

不可变:字符串、整数和元组

(回顾字典的key命名)

 

递归:

1 def calc(n):
2     print(n)
3     if int(n/2) == 0:
4         return n
5     return calc(int(n/2))
6 
7 calc(10)

 

递归特性:

1.必须有一个明确的结束条件

2.每次进入更深一层递归时,问题规模相比上次递归应有所减少。

3.递归效率不高,递归次数过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会多加一层栈帧,每当函数返回,栈就会减少一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2019-09-23 07:47  智超(Zhichao)  阅读(2236)  评论(1编辑  收藏  举报