19.python函数对象与闭包函数

关于闭包,即函数定义和函数表达式位于另一个函数的函数体内(嵌套函数)。而且,这些内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包


函数对象

精髓:把函数当成变量用(因为书写时多次运行所以案例返回的内存地址不相同),也可以像变量一样当做函数参数或者函数返回值使用

# 定义函数其实是 func 指向 函数存储的内存地址
def func():
    print('from func')

# 1.可以赋值
f = func # 注意:没有括号(有括号是函数的返回值赋值给f)
print(id(f),id(func)) # 14645184 14645184 内存地址相同
f() # from func 也可以调用f函数,效果相同

# 2.可以把函数当做参数,把对应的内存地址传给另一个函数
def foo(x):
    print(x) # <function func at 0x019677C0>
    x() # from func 可以加括号执行
foo(func) # 其实传输的是函数的内存地址

# 3.可以把函数当做另一个函数的返回值
def foo1(x):
    return x
res = foo1(func)
print(res) # <function func at 0x02208460>
res() # from func

# 4.可以当做容器类型的元素
l = [func,]
l[0]() # from func
dic = {'k1':func}
dic['k1']() # from func

函数对象的案例

修改ATM登录案例

# coding:utf-8

def login():
    print('登录功能')
def transfer():
    print('转账功能')
def check_banlance():
    print('查询余额')

def catalogue():
    # 读取func_dic来生成显示列表 -> 方便程序后期快速修改
    list = 0
    for name in func_dic:
        print('{} -> {}'.format(list,func_dic[name][0]))
        list += 1

func_dic = {
    # 程序运行过程中只有读的需求所以用元组
    '0':('退出',None), # 为了规范格式使用None
    '1':('登录',login), # 这里就是函数对象的应用
    '2':('转账',transfer),
    '3':('查询',check_banlance)
}

while True:
    catalogue() # 显示列表
    choic = input('请输入命令编号:').strip()
    if not choic.isdigit():
        print('输入有误')
        continue
    if choic == '0':
        break
    if choic in func_dic:
        func_dic[choic][1]()
    else:
        print('输入有误')

函数嵌套

函数嵌套在名称空间的‘嵌套关系’里面已经介绍过了

函数嵌套本质目的是为了将大功能再次才分为多个小功能,再由大功能调用小功能

函数嵌套调用:在函数中调用其他函数

def func():
    func1() # func函数中调用func1
def func1():
    pass
func()

案例:四个值查找最大值

def max(a,b):
    '''比较两个数大小'''
    if not a<b:
    # not a<b == a>b and a == b
        return a
    else:
        return b
def max4(a,b,c,d):
    res = max(a,b)
    res = max(res,c)
    res = max(res,d)
    return res
res = max4(1,5,3,4)
print(res)

函数嵌套定义:在函数内定义其他函数

把f1当做容器,全局无法调用f2

def f1():
    # f2存在于f1的局部名称空间,只能在f1内调用
    def f2():
        pass
    f2()

案例:求圆周面积

def circle(radius,action=0):
    '''求圆周长 (radius=半径长度,action=0求周长/1求面积)'''
    from math import pi
    def perimiter(radius):
        return 2*pi*radius
    def area(radius):
        return pi*(radius**2)
    if action==0:
        return perimiter(radius)
    elif action==1:
        return area(radius)

res = circle(5,1)
print(res)

闭包函数

闭包函数=名称空间与作用域+函数嵌套+函数对象

什么是闭包函数?

‘闭’:该函数是内嵌函数

‘包’:该函数包含对外层函数(不是全局函数)作用域名字的引用

def f1():
    x = 333
    def f2():
        print('函数f2:',x)
    return f2
f = f1() # 在全局拿到f2的内存地址
print(f) # <function f1.<locals>.f2 at 0x00AF85C8>
f() # 函数f2: 333

使用函数对象的概念,将处于嵌套函数内部的f2函数变成了全局函数f

但,无论在哪儿调用f,都不会改变f的状态

闭包案例

使用包requests(模仿浏览器进行http访问的模块)

pip install requests

传参爬网站html

import requests

# 方案一:传参 - 通过参数方式为函数体传值
def get(url):
    response = requests.get(url)
    print(response.text)
get('https://kinghtxg.github.io')

# 方案二:闭包 - 通过闭包的方式为函数体传值
def get(url):
    def get_url():
        response = requests.get(url)
        print(response.text)
    return get_url
baidu = get('https://www.baidu.com')
baidu()
blog = get('https://kinghtxg.github.io')
blog()
上一篇:P1091 [NOIP2004 提高组] 合唱队形(求升序序列的N^2复杂度的模板)


下一篇:二十八分钟,带你用gitlab向企业微信发出灵魂拷问