1、函数对象

函数名就是存放了函数的内存地址,存放了内存地址的变量都是对象,即 函数名 就是 函数对象

函数对象的应用场景

  • 可以直接被引用
  • 可以当作函数参数传递
  • 可以作为函数的返回值
  • 可以作为容器类型的元素
功能体
def add(n1, n2):
    return n1 + n2

def low(n1, n2):
    return n1 - n2

def jump(n1, n2):
    return n1 * n2

完成功能
def computed(n1, n2, fn):  # fn = add|low|jump
    res = fn(n1, n2)  # 调用具体的功能
    return res

功能对应关系
method_map = {  # 指令与函数对象的对应关系
    '1': add,
    '2': low,
    '3': jump
}

获取功能
def get_method(cmd):
    if cmd in method_map:
        return method_map[cmd]  # 返回 add|low|jump
    return add  # 当指令错误,add作为默认功能

while True:
    cmd = input('cmd: ')
    res = get_method(cmd)(10, 20)  # 根据指令获取功能并调用得到结果
    print(res)

2、函数的嵌套调用

函数的嵌套调用:在一个函数内部调用另一个函数

# 求两个数最大值
def max_two(n1, n2):
    if n1 > n2:
        return n1
    return n2

# 求三个数最大值
def max_three(n1, n2, n3):
    max = max_two(n1, n2)
    return max_two(max, n3)

# 求四个数最大值
def max_four(n1, n2, n3, n4):
    max = max_three(n1, n2, n3)
    return max_two(max, n4)
print(max_four(20, 50, 30, 50))

3、名称空间

名称空间:存放名字与内存空间地址对应关系的容器
作用:解决由于名字有限,导致名字重复发送冲突的问题 - 内置全局局部可以同时使用一个名字存放不同地址

三种名称空间
Built-in:内置名称空间;系统级,一个;随解释器执行而产生,解释器停止而销毁
Global:全局名称空间;文件级,多个;随所属文件加载而产生,文件运行完毕而销毁
Local:局部名称空间;函数级,多个;随所属函数执行而产生,函数执行完毕而销毁

加载顺序:Built-in > Global > Local
    -- 采用堆栈存储数据的方式(压栈),导致内置最后被访问

案例:

# part1
'''
x = 100
def fn():
    a = 10
fn()

# 没有缩进直接使用, 和在其他函数中,都只能使用x,不能使用函数内部的a
def func():
    print(x)
func()

# 问题点:什么导致变量的访问权限
'''

# part2
# 名称空间:存放变量名与栈区内存地址的对应关系
print(len('123'))
len = 10
print(len)
del len
print(len('abc'))
print(len)
len = 200
print(type(len))

def fn():
    len = 2000
    print(len)
fn()
print(len)

4、函数的嵌套定义

函数的嵌套定义:在函数内部定义函数
诞生的理由:一个函数想使用另一个函数内部的变量,可以定义在其内部

    def func():
        a = 10
        def fn():
            print(a)
        return fn

    new_fn = func()
    new_fn()

两个与函数有关的关键字:global nonlocal

    # global:统一局部与全局的变量名
    num = 10
    def outer():
        # global num
        # num = 100
        def inner():
            global num
            num = 1000

    # nonlcal: 统一局部与嵌套局部的变量名
    def outer():
        num = 100
        def inner():
            nonlocal num
            num = 1000

5、作用域

    # 作用域:名字起作用的范围
    # 作用:解决同名字可以共存问题 - 不同作用域相同名字的值都能在其作用域范围下进行使用
    '''
    四种作用域: LEGB
    Built-in:内置作用域 - 所有文件所有地方都可以被访问
    Global:全局作用域 - 在当前文件的所有位置
    Enclosing:嵌套作用域 - 自身内部与内部的子函数
    Local:局部作用域 - 只有自身内部
    '''
    # 加载顺序:Built-in > Global > Enclosing > Local
    # 访问(查找)顺序:报错 < Built-in < Global < Enclosing < Local
    # 作用范围:Built-in > Global > Enclosing > Local

6、闭包

闭包:定义在函数内部的函数,这个内部的函数就是闭包

应用场景:
1.可以去使用其他函数的内部变量,且还可以保证调用位置不变(闭包的函数对象作为那个函数的返回值)

    def outer():
        count = 3000
        def fn():
            print(count)  # 能使用outer内部的变量count
        return fn
    # 还是在外界调用
    outer()()  # outer()() => fn() => 调用fn

2.延迟执行(外层函数可以为内存函数传递参数)

    import requests
    def outer(url):
        def show_html():
            response = requests.get(url)
            print(response.text)
        return show_html

    # 制作 爬百度与新浪的 函数对象
    show_baidu = outer('https://www.baidu.com')
    show_sina = outer('https://www.sina.com.cn')

    # 延迟到需求来了,需要爬百度,就用百度函数对象,需要爬新浪,就用新浪函数对象
    show_baidu()
    show_sina()
    show_baidu()

7、装饰器

装饰器:装饰器就是闭包的一个应用场景
      -- 外层函数与内存函数形成的闭包结构的一种综合使用

重点:开放封闭原则
开放:拓展功能的点是开放的 - 可以为之前的函数添加新功能
封闭:1.不能改变原函数的源代码  2.还有通过原函数的函数对象来调用函数
def huaping():
    print('插花功能')

temp = huaping
def my_huaping():
    temp()
    print('观赏功能')
huaping = my_huaping

huaping()

# ----------------------------------------

def huaping():
    print('插花功能')

def outer(temp):  # temp = huaping
    def my_huaping():
        temp()
        print('观赏功能')
    return my_huaping
huaping = outer(huaping)  # huaping = my_huaping

huaping()


# ----------------------------------------------
def outer(temp):  # temp = huaping
    def my_huaping():
        temp()
        print('观赏功能')
    return my_huaping

@outer  # huaping = outer(huaping)
def huaping():
    print('插花功能')

huaping()

被装饰的函数可能有参有返:装饰器模板,可以满足所有参数,且能装饰原函数返回值

def outer(func):  # temp = huaping
    def inner(*args, **kwargs):
        pass
        res = func(*args, **kwargs)
           pass
        return res
    return inner

@outer
def any_method():
    pass

装饰器案例

# 为登录功能添加账号检验功能:必须是3个及以上英文字母组成
def check_user(func):
    def inner(user, pwd):
        if not (user.isalpha() and len(user) >= 3):
            return '账号不合法'
        res = func(user, pwd)
        return res
    return inner

# 为登录功能添加密码检验功能:必须是3个及以上英文字母或数字组成
def check_pwd(func):
    def inner(*args, **kwargs):
        pwd = args[1]
        if not (pwd.isalnum() and len(pwd) >= 3):
            return '密码不合法'
        res = func(*args, **kwargs)
        return res
    return inner

# 对登录结果的修饰装饰器:True=>登录成功 False=>登录失败
def change_res(func):
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        if res == True:
            return '登录成功'
        return '登录失败'
    return inner


# 装饰器被执行的过程是从上至下
@check_user  # login = check_user(func=login) = inner
@check_pwd
@change_res
def login(user, pwd):  # 被装饰的函数对象
    if user == 'owen' and pwd == '123':
        return True
    return False

user = input('user: ')
pwd = input('pwd: ')
res = login(user, pwd)

print(res)

带参数的装饰器

# 为什么要出现带参装饰器
def outer(func):
    # outer与inner之间要使用外部数据
    # 可以解决的方案路径,给outer添加参数,但是outer的参数是固定一个,就是被装饰的函数
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    return inner

# 所以只能使用函数的闭包,通过外层函数给内存函数传递参数的方式
def wrap(*arg, **kwargs):
    def outer(func):
        # 就可以使用wrap中的*arg, **kwargs,就是要使用的外部数据
        def inner(*args, **kwargs):
            res = func(*args, **kwargs)
            return res
        return inner
    return outer

a = 10
b = 20
@wrap(a, b)  # @wrap(10, 20) => @outer => fn = outer(fn) => fn = inner
def fn():
    pass

wraps修改函数文档注释

# 为什么要出现该语法

from functools import wraps
def outer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        res = func(*args, **kwargs)
        return res
    return inner
def fn():
    '''
    fn的文档注释
    '''

print(fn.__doc__)  # fn本质是inner,使用打印fn.__doc__本质是inner函数的文档注释

# 形参假象:让打印fn.__doc__显示的效果是fn自己的
文档更新时间: 2019-04-22 10:53   作者:李延召