0
点赞
收藏
分享

微信扫一扫

Python 函数式编程(二)

Python百事通 2021-09-28 阅读 62

一. 变量及作用域

局部变量:局部变量是指定义在函数内部的变量,函数的形参也属于局部变量。局部变量只能在函数内部使用,并在函数调用时被创建,调用结束后自动销毁。

全局变量:全局变量是指定义在函数外部,模块内部的变量。所有的函数都可以直接 访问 全局变量,而只有可变数据类型的全局变量才可以在函数中被修改。

Python的作用域:Python 中的作用域可以简称为 LEGB,也叫命名空间,是访问变量时查找变量名的范围空间。变量名的查找顺序即:L --> E --> G --> B

  • L(local):局部作用域,即函数内部作用域;
  • E(Enclosing):外部嵌套函数作用域;
  • G(Globals):全局作用域,即模块内部作用域;
  • B(Builtin):Python 内置模块的作用域。

global 语句:即全局声明,将函数内部赋值的变量映射到全局作用域。

在函数内部修改绑定不可变数据类型的全局变量时,必须用 global 声明,否则将被视为局部变量:

GLOBAL_STR = 'HELLO PYTHON'

def my_print():
    GLOBAL_STR = 'hello python'
    print(GLOBAL_STR)

不使用 global 时,在 my_print 中对 GLOBAL_STR 赋值,其实是在函数内部作用域创建了一个新的变量 GLOBAL_STR,原 GLOBAL_STR 并不会发生变化:

>> my_print()
hello python
>> GLOBAL_STR
'HELLO PYTHON'

全局变量在函数内部可以不经过声明直接访问:

GLOBAL_STR = 'HELLO PYTHON'

def my_print():
    print(GLOBAL_STR)

运行结果:

>> my_print()
HELLO PYTHON

禁止先创建局部变量,再用 global 声明为全局变量:

nonlocal 语句:nonlocal 声明,指出变量不是局部变量,而是外部嵌套函数作用域(Enclosing)的变量。

nonlocal 语句只能在内嵌函数内使用;访问 nonlocal 变量将对外部嵌套函数作用域内的变量产生影响。当有 2 层或 2 层以上的函数嵌套时,访问 nonlocal 变量只对最近一层的变量进行操作。

二. 闭包

闭包满足的三个条件

  • 必须有内嵌函数
  • 内嵌函数必须引用外部嵌套函数(Enclosing)作用域中的变量
  • 外部函数返回值必须是 内嵌函数

通常情况下,在函数调用结束后,函数内部变量会自动被释放。但闭包不同,如果外部嵌套函数在调用结束后,发现自己的变量在内嵌函数中有使用,则外部函数在调用结束后,在返回内嵌函数的同时,也会将这些变量传递给内嵌函数,一起绑定起来。

因此,外部函数调用结束后,内嵌函数被调用时仍可以访问外部嵌套函数作用域内的变量。

闭包演示

def outer(start, step1=0, step2=None):
    i = 0
    if step2 is None:
        step2 = [step1]
    def iner():
        nonlocal step1, i
        print('{} + {} = {}'.format(start, step1, start + step1))
        print('{} + {} = {}'.format(start, step2[i], start + step2[i]))
        step1 = step1 * 2
        step2.append(step2[i] * 2)
        i += 1
    return iner

上述代码中,istep1 都是 Enclosing 作用域的变量,并且绑定不可变数据类型。因此想要在 inner 函数中修改他们(重新绑定新的对象),必须使用 nolocal 语句将 i step1 声明名 nolocal 变量。

step2 虽然也是 Enclosing 作用域的变量,但绑定的列表,属于可变数据类型。因此,可以在 innner 中修改 step2 绑定的对象内容。但注意,如果要为 step2 变量绑定全新的对象,仍需要 nolocal 语句声明。

运行结果

>> my_sum = outer(10, step1=10)
>> my_sum()
10 + 10 = 20
10 + 10 = 20
>> my_sum()
10 + 20 = 30
10 + 20 = 30
>> my_sum()
10 + 40 = 50
10 + 40 = 50

每次的调用 my_sum 打印的值都不同,因此闭包可以用作记录程序的内部状态。

三. 递归函数 Recursion

递归函数只函数直接或间接地调用自身。如:

def func():
    '''直接调用自己,进入递归'''
    func()

def func1():
    '''函数间接调用自身'''
    func2()

def func2():
    func1()

调用上述函数时,将在达到系统最大的递归深度时抛出异常:

>> func()
...
RecursionError: maximum recursion depth exceeded
>> func1()
...
RecursionError: maximum recursion depth exceeded

使用 sys.getrecursionlimit() / sys.setrecursionlimit(n) 即可查看或修改系统默认的递归深度:

>> import sys
>> sys.getrecursionlimit()
3000

递归分为以下两个阶段:

  • 递推阶段:由问题出发,从未知到已知,按递归公式递推,直到满足终止条件。
  • 回归阶段:由递归终止条件得到的结果出发,逆向逐步代入递归公式,回归问题。

在适合使用递归的问题上,使用递归,可以简化问题,使得思路清晰,代码简洁。比如,下面几个递归的应用就很恰到好处~

计算等差数列:1, 2, 3 ... n 的和:

def mysum(n):
    if n == 1:
        return 1
    return n + mysum(n-1)

运行结果:

>> mysum(100)
5050

写一个函数求 n 的阶乘:

def myfac(n):
    if n==1:
        return 1
    return n * myfac(n-1)

运行结果:

>> print('5的阶乘是:', myfac(5))
5的阶乘是: 120

已知嵌套列表:L = [[3, 5, 8], 10, [[13, 14], 15, 18], 20],递归打印列表内的所有元素,并计算列表中所有元素的和。

def print_list(L):
    for item in L:
        if type(item) is not list:
            print(item, end=' ')
        else:
            print_list(item) # 递归的思想
 
def sum_list(L):
    sum_number = 0
    for item in L:
        if type(item) is not list:
            sum_number += item
        else:
            sum_number += sum_list(item)
    return sum_number

运行结果:

>> L = [[3, 5, 8], 10, [[13, 14], 15, 18], 20]
>> print_list(L)
3 5 8 10 13 14 15 18 20
>> print("返回这个列表中所有元素的和:", sum_list(L))
返回这个列表中所有元素的和: 106

小结:递归的实现方法,即先假设函数已经实现,找出递归的终止条件。

举报

相关推荐

0 条评论