07.函数

函数

  • 为什么要创建函数
  • 函数的概念
  • 如何创建一个函数
  • 为函数添加文档注释,以及如何获取文档注释
  • 函数如何返回一个值
  • 改变函数的参数值
  • 关键字参数与参数默认值
  • 可变参数
  • 如何将序列中的元素单独作为参数值从传递给函数
  • 函数中的作用域
  • 递归函数
# 斐波那锲数列
fibs = [0,1]
for i in range(10):
    fibs.append(fibs[-2] + fibs[-1])
print(fibs)    
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
fibs = [0,1]
n = int(input('请输入一个整数:'))
for i in range(n):
    fibs.append(fibs[-2] + fibs[-1])
print(fibs)
请输入一个整数:20
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946]
# 创建和使用函数
# if while for
# def funName(param1,param2,...param):
#  statement1
#  statement2
# statement3

def fibs(n):
    result = [0,1]
    for i in range(n):
        result.append(result[-2] + result[-1])
    return result
print(fibs(9))
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
while True:
    n = input('请输入一个整数:')
    if n == ':exit':
        break;
    n = int(n)
    print(fibs(n))
请输入一个整数:12
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]
请输入一个整数::exit
# 为函数添加文档注释

def add(x,y):
    '计算两个数的和'
    return x + y
print(add(1,2))

print(add.__doc__)
3
计算两个数的和
help(add)
Help on function add in module __main__:

add(x, y)
    计算两个数的和

没有返回值的函数

  • Pascal语言中称没有返回值的函数为过程。
  • 1.没有return语句
  • 2.有return语句的话,后面没有任何值
  • 3.没有返回值的函数返回的是None
def fun1(flag):
    if flag:
        print("满足条件")
    else:
        print("不满足条件")
fun1(True)        
满足条件
def fun2(flag):
    if flag:
        return
    print("满足条件")
fun2(True)
fun2(False)
print(fun1(False))
满足条件
不满足条件
None
不满足条件
def fun3(flag):
    if flag:
        return 20
    else:
        return
print(fun3(True))
print(fun3(False))
20
None

改变函数参数的值

  • 值传递(数值、字符串、布尔等类型),函数内部修改变量值,不会影响函数外部的变量。
  • 引用传递(复合类型:列表、字典、对象),函数内部修改变量值,会影响函数外部的变量。
  • 改变函数参数值(案列)
# 值传递(函数内部修改变量值,不会影响函数外部的变量)
x = 100
s = 'hello world'
def test1(xx,ss):
    xx = 200
    ss = 'I love you.'
test1(x,s)
print(x,s)
100 hello world
# 引用传递(复合类型:列表、字典、对象),函数内部修改变量值,会影响函数外部的变量。
numList = [1,2,3,4,5]
numDict = {'a':1,'b':2,'c':3}
a = numList
a[1] = 4
print(numList)
def test2(nl,nd):
    nl[2] = 1234
    nd['c'] = 4321
test2(numList,numDict)
print(numList,numDict)    
[1, 4, 3, 4, 5]
[1, 4, 1234, 4, 5] {'a': 1, 'b': 2, 'c': 4321}
# 改变函数参数值(案列)
# 从控制台输入一些数据(以逗号分隔的字符串),然后保存到一个大的字典中。
data = {}
data['d'] = {}
data['names'] = []
print('请输入字典数据,key和value之间用逗号分隔')
dictStr = input(':')
# a,b,c,d 转成如下:
# {'a':'b','c':'d'}
list = dictStr.split(',')
keys = []
values = []
for i in range(len(list)):
    if i % 2 == 0:
        keys.append(list[i])
    else:
        values.append(list[i])
data['d'].update(dict(zip(keys,values)))

print('请输入姓名,多个姓名之间用逗号分隔')
nameStr = input(':')
names = nameStr.split(',')
data['names'].extend(names)

print(data)

请输入字典数据,key和value之间用逗号分隔
:a,1,b,2,c,3,d,4,e,5
请输入姓名,多个姓名之间用逗号分隔
:Bill,Mike,John
{'d': {'a': '1', 'b': '2', 'c': '3', 'd': '4', 'e': '5'}, 'names': ['Bill', 'Mike', 'John']}

如果从功能上看,上面的代码实现的很完美,如果要对多个字典进行同样的操作,要将这些代码复制多份太麻烦,而且会造成代码极大的冗余。可以用函数对这段代码进行抽象,将经常使用的代码段提炼处理封装在函数中。

  • 在抽象代码之前,要先看看有哪些代码可以被抽象出来。本例可以抽象出来的代码有如下几种:
    • 初始化字典data。
    • 从控制台输入以逗号分隔的字符串,并将其转换成列表或字典。
    • 输出字典data。
# 1.初始化字典data
def init(data):
    data['d'] = {}
    data['names'] = []
# 2.从控制台输入以逗号分隔的字符串,并将其转换成列表或字典,flag为True转换为列表,flag为False转换为字典;msg表示提示文本,
# 为了方便,这里假设输入的数据以逗号分隔,也可以将分隔符通过函数参数传入。
def inputListOrDict(flag,msg):
    print(msg)
    inputStr = input(':')
    list = inputStr.split(',')
    # 直接返回列表
    if flag:
        return list
    keys = []
    values = []
    result = {}
    for i in range(len(list)):
        if i % 2 == 0:
            keys.append(list[i])
        else:
            values.append(list[i])
    return dict(zip(keys,values))
def outDict(data):
    for key in data.keys():
        print(key,':',data[key])

        
from dataman import *

data1 = {}
data2 = {}
init(data1)
init(data2)

data1['d'].update(inputListOrDict(False,'请输入字典数据'))
data1['names'].extend(inputListOrDict(True,'请输入列表数据'))

data2['d'].update(inputListOrDict(False,'请输入字典数据'))
data2['names'].extend(inputListOrDict(True,'请输入列表数据'))

outDict(data1)
outDict(data2)        
请输入字典数据
:a,1,b,2,c,3
请输入列表数据
:Bill,Mike,Mary
请输入字典数据
:new,新的,my,我的,China,中国
请输入列表数据
:Mobile,car,plane
d : {'a': '1', 'b': '2', 'c': '3'}
names : ['Bill', 'Mike', 'Mary']
d : {'new': '新的', 'my': '我的', 'China': '中国'}
names : ['Mobile', 'car', 'plane']

关键字参数和参数默认值

  • 到目前为止,函数的参数位置很重要,因为在调用函数时,传递实参时都是按照形参的定义顺序传递的。
    • 1.在调用函数时,实参的顺序与形参严重相关。为了抵消这种相关性,在调用函数时可以用关键字指定参数,这种参数被称为关键字参数。
    • 2.关键字参数也可以与位置参数混合使用,在混合使用时:(1)关键字参数必须放在位置参数后面,否则会抛出异常;(2)只能将位置参数还未设置的参数作为关键字参数指定,也就是同一个形参不能同时使用位置参数和关键字参数赋值。
    • 3.如果函数的参数过多,或者在特定的场景,大多数参数都使用某个固定的值即可,那么可以为函数的形参指定默认值,如果不指定形参的值,那么函数就会使用形参的默认值。
    • 4.函数参数默认值,如果一个参数有默认值,那么该参数后面的所有参数必须都有默认值,否则会抛出异常。
# 例子:
def greet(name,greeting):
    return "问候语:{} 姓名:{}".format(greeting,name)
print(greet('钟南山','Hello'))
print(greet('Hello','钟南山')) # 不会抛出异常,但是输出的内容并不符合要求。
问候语:Hello 姓名:钟南山
问候语:钟南山 姓名:Hello
# 用关键字指定参数
print(greet(name = "钟南山",greeting = "Hello"))
print(greet(greeting = "Hello",name = "钟南山"))
问候语:Hello 姓名:钟南山
问候语:Hello 姓名:钟南山
# 关键字参数与位置参数混合使用。
print(greet("钟南山",greeting = "Hello"))
问候语:Hello 姓名:钟南山
print(greet(name = "钟南山","Hello")) # 关键字参数必须放在位置参数后面,否则会抛出异常。
  File "<ipython-input-18-4a2a6fdf0d18>", line 1
    print(greet(name = "钟南山","Hello")) # 关键字参数必须放在位置参数后面,否则会抛出异常。
                                  ^
SyntaxError: positional argument follows keyword argument
def greet(name = "Bill",greeting = "Hello"):
    return "问候语:{} 姓名:{}".format(greeting,name)
greet() # 未指定任何参数值,name参数和greeting参数都会使用各自的默认值。
'问候语:Hello 姓名:Bill'
greet(name = "Jackson") # 可以指定一个参数值,未指定参数值使用默认值
'问候语:Hello 姓名:Jackson'
greet(greeting = "你好")
'问候语:你好 姓名:Bill'
greet("Hello",name = "Jackson")# 同一个形参不能同时使用位置参数和关键字参数赋值
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-42-d667ef3def0a> in <module>
----> 1 greet("Hello",name = "Jackson")


TypeError: greet() got multiple values for argument 'name'
# 函数参数默认值,如果一个参数有默认值,那么该参数后面的所有参数必须都有默认值,否则会抛出异常。
def fun(a,b,c=123,d=12,e=15,f=20,g=40):
    print(a,b,c,d,e,f,g)
fun(1,2,3,4,5)
1 2 3 4 5 20 40
def fun(a,b,c=123,d,e,f=20,g=40):
    print(a,b,c,d,e,f,g)
fun(1,2,3,4,5)
  File "<ipython-input-50-72adcde896f6>", line 1
    def fun(a,b,c=123,d,e,f=20,g=40):
           ^
SyntaxError: non-default argument follows default argument
  • 例题
    • 编写两个函数:sub1和sub2。这两个函数的功能相同,只是sub1未指定参数默认值,而sub2指定了参数默认值。我们会使用这两个函数展示关键字参数和参数默认值的各种用法。
def sub1(m,n):
    return m - n
# 使用位置参数传递参数值
print(sub1(20,4),end=",")
print(sub1(4,20))
16,-16
# 使用关键字参数传递参数值
print(sub1(m = 20, n =4),end=",")
print(sub1(n = 4, m = 20))
16,16
def sub2(m = 100, n = 50):
    return m - n
# 调用sub2时未指定任何参数值
print(sub2())
50
# 调用sub2时使用了位置参数
print(sub2(99,66))
33
# 调用sub2时使用了混合参数模式
print(sub2(715, n = 814))
-99
# 调用sub2时使用了关键字参数,m仍然使用默认值
print(sub2(n = 814))
-714
# 调用sub2时使用了关键字参数
print(sub2(m = 715, n = 814))
-99
# 尽管关键字参数在位置参数后面使用,但产生了歧义,系统不知道m的值应该是715还是814,所以会抛出异常
print(sub2(715, m = 814))
---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-11-b40095daaf52> in <module>
      1 # 尽管关键字参数在位置参数后面使用,但产生了歧义,系统不知道m的值应该是715还是814,所以会抛出异常
----> 2 print(sub2(715, m = 814))


TypeError: sub2() got multiple values for argument 'm'

可变参数

  • 1.像print函数这样可以传递任意多个参数的形式称为可变参数。定义函数的可变参数需要在形参前面加一个星号(*),表明该参数是一个可变参数,在调用函数时可以指定任意多个参数,而且参数类型也可以是任意的。可变参数在函数内部是以元组的形式体现的,所以在函数内部可以像元组一样使用可变参数中的具体参数值。
  • 2.使用可变参数需要考虑形参位置的问题。如果在函数中,既有普通参数,也有可变参数,通常可变参数会放在最后。其实可变参数也可以放在函数参数的中间或最前面,只是在调用函数时,可变参数后面的普通参数要使用关键字参数形式传递参数值。
  • 3.如果可变参数在函数参数的中间位置,而且在为可变参数后面的普通参数传值时也不想使用关键字参数,那么就必须为这些普通参数指定默认值;如果不为这些普通形参指定默认值,在调用函数时也未使用关键字参数指定这些形参的值,那么就会抛出异常。
# 定义一个带有可变参数的函数
def printParams(*params):
    print(params)
printParams("Hello","girl",520,True,3.14)    
('Hello', 'girl', 520, True, 3.14)
# 通过for语句枚举了可变参数中所有参数值,并为每一个参数值两侧加上一对尖括号(<...>)
def printHearts(*hearts):
    for heart in hearts:
        print("<" + str(heart) + ">", end = " ")
printHearts("Hello","girl",520,True,3.14)      
<Hello> <girl> <520> <True> <3.14> 
# 使用可变参数需要考虑形参位置的问题。如果在函数中,既有普通参数,也有可变参数,通常可变参数会放在最后。
def printHearts(value,*hearts):
    print("[" + value + "]")
    for heart in hearts:
        print("<" + str(heart) + ">", end = " ")
# 调用printHearts函数
printHearts("Love","girl",520,1314,True,10000.99)
[Love]
<girl> <520> <1314> <True> <10000.99> 
# 其实可变参数也可以放在函数参数的中间或最前面,只是在调用函数时,可变参数后面的普通参数要使用关键字参数形式传递参数值。
def printHearts(value1,*hearts,value2,value3):
    print("[" + value1 + "]")
    for heart in hearts:
        print("<" + str(heart) + ">", end = " ")
    print("{},{}".format(value2,value3))
# 调用printHearts函数,value2和value3必须使用关键字参数形式指定参数值
printHearts("Love","girl",520,1314,True,10000.99,value2 = 715,value3 = 814)
# 如果不为这些普通形参指定默认值,在调用函数时也未使用关键字参数指定这些形参的值,那么就会抛出异常。
printHearts("Love","girl",520,1314,True,10000.99,715,814)        
[Love]
<girl> <520> <1314> <True> <10000.99> 715,814



---------------------------------------------------------------------------

TypeError                                 Traceback (most recent call last)

<ipython-input-18-ddbf6537d7a5> in <module>
      7 # 调用printHearts函数,value2和value3必须使用关键字参数形式指定参数值
      8 printHearts("Love","girl",520,1314,True,10000.99,value2 = 715,value3 = 814)
----> 9 printHearts("Love","girl",520,1314,True,10000.99,715,814)


TypeError: printHearts() missing 2 required keyword-only arguments: 'value2' and 'value3'
# 如果可变参数在函数参数的中间位置,而且在为可变参数后面的普通参数传值时也不想使用关键字参数,那么就必须为这些普通参数指定默认值。
def printHearts(value1,*hearts,value2 = 715, value3 = 814):
    print("[" + value1 + "]")
    for heart in hearts:
        print("<" + str(heart) + ">", end = " ")
    print("{},{}".format(value2,value3))
# 调用printHearts函数,value2和value3必须使用关键字参数形式指定参数值
printHearts("Love","girl",520,1314,True,10000.99)
[Love]
<girl> <520> <1314> <True> <10000.99> 715,814
  • 例题
    • 本例编写了4个函数:addNumbers,calculator,calculator1和calculator2。其中,addNumbers函数用于计算多个数值之和。该函数只有一个可变参数numbers,要求传入numbers的参数值是数值类型。calculator、calculator1和calculator2函数的功能类似,都用于计算传入可变参数值的加(Add)、减(sub)、乘(Mul)和除(Div)。calculator函数在可变参数前面加了一个普通形参type,用来指定执行的是哪种操作。calculator1函数在可变参数后面指定了一个普通形参ratio,calculator2函数与calculator1函数的参数类似,只是为ratio参数指定了一个默认值。通过这4个函数的定义和调用方式,可以完全了解如何使用普通形参、关键字参数和参数默认值来定义和调用函数。
# 定义addNumbers函数,该函数指定了一个可变参数numbers
def addNumbers(*numbers):
    result = 0
    # 枚举numbers中所有参数值,并将这些值累加,存储到result变量中
    for number in numbers:
        result += number
    # 返回累加和
    return result

# 调用addNumbers函数
print(addNumbers(1,2,3,4,5))
print("........")
# 定义calculator函数,在可变参数numbers前面加一个普通的形参type
def calculator(type,*numbers):
    result = 0
    # type参数的值可为add、sub、mul和div,分别表示加、减、乘和除
    # 进行加法计算
    if type == "add":
        for number in numbers:
            result += number
    # 进行减法计算
    elif type == "sub":
        result = numbers[0]
        for i in range(1,len(numbers)):
            result -= numbers[i]
    elif type == "mul":
        result = 1
        for number in numbers:
            result *= number
    else:
        result = numbers[0]
        for i in range(1,len(numbers)):
            result /= numbers[i]
    return result 


# 调用calculator函数,会将第1个实参作为type参数的值传给calculator函数
print(calculator("add",1,2,3,4,5,6),end=",")
print(calculator("sub",100,10,20,30,40),end=",")
print(calculator("mul",1,2,3,4,5,6,7),end=",")
print(calculator("div",100,2,25,30))
                
15
........
21,0,5040,0.06666666666666667
# 定义calculator1函数,在可变参数numbers后面指定了一个普通的形参ratio
def calculator1(type,*numbers,ratio):
    # 在调用另一个函数时,如果为该函数的可变参数传入另一个可变参数值,也要在参数名前面加星号(*)
    return calculator(type,*numbers) * ratio
# 调用calculator1函数,使用关键字参数指定了ratio参数的值
print(calculator1("add",1,2,3,4,5,6,ratio = 3),end=",")
print(calculator1("sub",100,10,20,30,40,ratio = 2),end=",")
print(calculator1("mul",1,2,3,4,5,6,7,ratio = 4),end=",")
print(calculator1("div",100,2,25,30,ratio = 2))
63,0,20160,0.13333333333333333
# 定义calculator2函数,为ratio参数指定了一个默认值
def calculator2(type,*numbers,ratio = 4):
    # 在调用另一个函数时,如果为该函数的可变参数传入另一个可变参数值,也要在参数名前面加星号(*)
    return calculator(type,*numbers) * ratio
# 调用calculator2函数,没有为ratio参数指定新的值。
print(calculator2("add",1,2,3,4,5,6))
84

将序列作为函数的参数值

  • 1.函数参数值可以是任何数据类型,自然也包括序列(元组、列表、字典等)。不过本节讲的并不是直接将序列作为单个的参数值传入函数,而是将序列中的每个元素单独作为函数的参数值,相当于把序列拆开进行传值,但是需要在列表前面加(*)。
  • 2.函数如果使用可变参数,也可以通过列表或元组参数传值。不仅可以将列表变量前面加()后传入函数,而且也可以将列表值前面加()传入函数。
  • 3.在传递参数时,字典和列表(元组)的区别是字典前面需要加两个星号(*)(定义函数和调用函数都需要加两个星号),而列表(元组)前面只需要加一个星号。
  • 4.在函数中使用字典的方式,加两个星号与不加星号完全相同。如果在定义函数时,参数未加两个星号,那么在调用该函数时,也不能加两个星号。
# 将序列中的每个元素单独作为函数的参数值,相当于把序列拆开进行传值,但是需要在列表前面加(*)。
def printParams(s1, s2):
    print(s1, s2)

printParams("Hello","world")
list = ["Hello", "world"]
printParams(*list)
Hello world
Hello world
# 函数如果使用可变参数,也可以通过列表或元组参数传值。不仅可以将列表变量前面加(*)后传入函数,而且也可以将列表值前面加(*)传入函数。

def printParams(*ss):
    for s in ss:
        print("<{}>".format(s),end = ' ')

list = ["Hello","world"]
printParams(*list)
print()
printParams(*"abcdefg")
print()
printParams(*[1,2,3,4])
print()      
<Hello> <world> 
<a> <b> <c> <d> <e> <f> <g> 
<1> <2> <3> <4> 
# 在传递参数时,字典和列表(元组)的区别是字典前面需要加两个星号(*)(定义函数和调用函数都需要加两个星号),而列表(元组)前面只需要加一个星号。

def printParams(**ss):
    for item in ss.items():
        print("{} = {}".format(item[0],item[1]))
        
dict = {'a':40,'b':50,'c':12} 
printParams(**dict)
printParams(**{"name":"Bill","age":23})
a = 40
b = 50
c = 12
name = Bill
age = 23
# 在函数中使用字典的方式,加两个星号与不加星号完全相同。如果在定义函数时,参数未加两个星号,那么在调用该函数时,也不能加两个星号。

def printParams(ss):
    for item in ss.items():
        print("{} = {}".format(item[0],item[1]))
        
dict = {'a':40,'b':50,'c':12} 
printParams(dict)
printParams({"name":"Bill","age":23})
a = 40
b = 50
c = 12
name = Bill
age = 23
  • 例题
    • 本例通过add1、add2、add3和add4四个函数对如何使用列表和字典中单个元素作为函数参数传递进行了完整的演示。
def add1(x,y,z):
    return x + y + z
print(1,2,3)
1 2 3
list = [2,3,4] # 定义一个列表,也可以使用元组。
print(add1(*list))
9
dict = {'x':100,'y':200,'z':12}
# 将字典中的x、y、z拆分成名为x、y、z的3个形参值,然后传入函数。
print(add1(**dict))
312
# 用可变参数定义函数
def add2(*numbers):
    result = 0
    for number in numbers:
        result += number
    return result
print(add2(1,2,3,4,5))
15
# 使用星号同样可以拆分列表,并将单个元素作为参数传入add2的可变参数中
print(add2(*list))
9
# 定义add3函数时,numbers参数前使用两个星号,表示这个参数值接收字典类型数据
def add3(**numbers):
    result = 0
    for item in numbers.items():
        # 将numbers字典中的所有value相加
        result += item[1]
    return result
# 将字典dict中的元素作为单独的参数传入了add3函数
print(add3(**dict))       
312
def add4(numbers):
    result = 0
    for item in numbers.items():
        result += item[1]
    return result
# 如果在定义函数时不加双星,那么在调用时也不需要加。
print(add4(dict))
312

作用域

  • 1.作用域就是变量、函数、类等Python语言元素是否可见的范围。直接在Python文件的顶层定义的变量、函数都属于全局作用域,而在函数中定义的变量属于函数本身的局部作用域。在局部作用域中定义的变量,在上一层作用域是不可见的。
  • 2.在局部作用域中也可以访问上一层作用域中的变量(这里是全局作用域),但不能在局部的作用域中定义同名的变量。
  • 3.在Python语言中,不管在作用域的哪个位置为变量赋值,都会认为这个变量属于当前作用域,而且会隐藏上层作用域同名的变量。
  • 4.在Python语言中,函数支持嵌套,也就是说,可以在一个函数中定义另一个函数,并且可以直接返回函数本身(相当于C语言中返回函数的指针)。
# 在全局作用域中定义了一个变量x,该变量的值是1,在fun1函数中也定义了一个变量x,该变量的值是30。
# 其实这两个变量是完全不同的。在fun1函数中只能看见x等于30的局部变量,而在全局作用域中,也只能
# 看到x等于1的全局变量。
x = 1
def fun1():
    x = 30
fun1()
print(x)
1
# 在局部作用域中也可以访问上一层作用域中的变量(这里是全局作用域),但不能在局部的作用域中定义同名的变量。
# 之所以在fun2函数中可以访问全局变量x,是因为在fun2函数中并没有定义局部变量。
x = 123
def fun2():
    print(x)
fun2()    
123
# 在fun3函数中定义了一个局部变量,所以全局变量隐藏了,在fun3函数中将无法访问全局变量x,只能访问局部变量x。
x = 123
def fun3():
    x = 30
    print(x)
fun3()
30
# 在Python语言中,不管在作用域的哪个位置为变量赋值,都会认为这个变量属于当前作用域,
# 而且会隐藏上层作用域同名的变量。x不会使用全局变量x,而仍然会使用局部变量x,但是x是在print(x)后面赋值的,所以
# 会抛出异常。
x = 123
def fun4():
    print(x)
    x = 30
fun4()
---------------------------------------------------------------------------

UnboundLocalError                         Traceback (most recent call last)

<ipython-input-8-db3a66b69d47> in <module>
      6     print(x)
      7     x = 30
----> 8 fun4()


<ipython-input-8-db3a66b69d47> in fun4()
      4 x = 123
      5 def fun4():
----> 6     print(x)
      7     x = 30
      8 fun4()


UnboundLocalError: local variable 'x' referenced before assignment
# 本例中定义了一个全局变量x,在函数fun5中定义了一个局部变量x,
# 在嵌套函数fun6中访问的是在fun5中定义的局部变量x。
# 也就是说,如果在当前函数中没有定义局部变量x,那么当前函数会从上一个作用域开始查找,
# 直到找到变量x或到了全局作用域为止。

x = 30
def fun5():
    x = 40
    def fun6():
        print(x)
        print("fun6")
    return fun6
fun5()()
40
fun6

递归

  • 1.所谓递归,就是在函数内部调用自身。在执行过程中,Python解析器会利用栈(stack)处理递归函数返回的数据。所以递归函数的一个必要条件是要有终止条件,否则栈就会溢出。
  • 2.通过递归可以实现很多经典的算法,如阶乘、斐波那契数列等。

例题:本例将通过递归实现阶乘和斐波那契数列。
假设计算阶乘的函数是jc(n),其中n是整数型参数,该函数表示计算n的阶乘。如果要计算n的阶乘,从数学上描述有如下的公式:n!= 1 * 2 * 3…* n,该公式也可以按如下形式描述:n!=n*(n-1)!,如果用jc函数描述,会有如下的表达式:jc(n) = n * jc(n-1)。
其实这就是一个典型的递归表达式,n!等于(n-1)!与n的乘积,而要计算(n-1)!,就需要计算(n-1) * (n-2)!。依此类推,计算1!,会有1! = 1 * (1-1)! = 1 * 0! = 1。由于0!等于1,所以0就是阶乘的终止条件,当然,有时也可以将0和1都作为阶乘的终止条件。也就是说,在递归函数中,先要考虑终止条件,然后再进行递归调用。斐波那契数列与阶乘的思路类似,也需要考虑终止条件,而斐波那契数列的终止条件是n等于1或2时,因为斐波那契数列需要从第3个值开始,才可以用前面两个值的和作为当前值。而n等于1时数列值是0,n等于2是数列值是1。

# 计算阶乘的递归函数
def jc(n):
    # 终止添加
    if n == 0 or n == 1:
        return 1
    else:
        # 进行递归调用
        return n * jc(n - 1)
print(jc(10))            
3628800
# 计算斐波那契数列的递归函数
def fibonacci(n):
    # 终止条件
     if n == 1:
            return 0
    # 终止条件
     elif n == 2:
            return 1
     else:
        # 进行递归调用
            return fibonacci(n - 1) + fibonacci(n - 2)
print(fibonacci(10))
34

实战与练习

1.编写一个名为sortNumbers的Python函数,该函数有两个参数,其中一个是可变参数numbers,另一个是普通的形参type。该形参的默认值是asc.函数的功能是按升序或降序排列可变参数numbers中的参数值,并以列表形式返回排序结果。type参数指定该函数时按升序还是按降序排列,type参数值为asc表示按升序排列,其他的值为降序排列。

def sortNumbers(*numbers,type = 'asc'):
    if type == 'asc':
        return sorted(numbers,reverse = False)
    else:
        return sorted(numbers,reverse = True)
print(sortNumbers(1,2,3,5,1,5,7,4,5))
print(sortNumbers(1,2,3,5,1,5,7,4,5,type = 'desc'))
[1, 1, 2, 3, 4, 5, 5, 5, 7]
[7, 5, 5, 5, 4, 3, 2, 1, 1]
  1. 编写一个递归的Python函数来实现二分查找。如果在有序列表中查到了指定值,返回该值在列表中的索引,否则返回-1。
def search(sequence,number,lower = 0 ,upper = None):
    if upper is None: upper = len(sequence) - 1
    # 终止条件    
    if lower == upper:
        if number == sequence[upper]:
            return upper   
        else:
            return -1
    else:
        middle = (lower+upper)//2 # 把序列一刀分为两部分
        # 要查找的值在右半部
        if number > sequence[middle]:
            return search(sequence,number,middle + 1,upper)
        else:
            return search(sequence,number,lower,middle)
list = [1,4,6,8,9,12,45,97]
print(search(list,45))
6

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值