Python函数基础3 函数对象、名称空间、装饰器

今日内容

  • 1.函数对象

  • 2.名称空间与作用域

  • 3.函数的嵌套调用与闭包

  • 4.装饰器

一、函数对象

1.1 定义

函数名存放的就是函数地址,所以函数名也就是对象,称之为函数对象

1.2 函数对象的应用

1.可以直接被引用
2.可以当作函数参数传数
3.可以作为函数的返回值
4.可以作为容器类型的元素
def fn():
    num = 10
    print('fn function run')
    
# 直接被引用
func = fn
fn()
func()

案例:四则运算

def add(n1, n2):
    return n1 + n
def sub(n1, n2):  # subtraction 减法
    return n1 - n2
def mul(n1, n2):  # multiplication 乘法
    return n1 * n2
def div(n1, n2):  # division 除法
    return n1 / n2
    
method_map = {
    'add': add,
    'sub': sub,
    'mul': mul,
    'div': div
}

def computed(fn):
    if fn in method_map:
        return method_map[fn]
    else:
        return add
def func():
    cmd = input("方法名:")
    method = computed(cmd)
    result = method(100, 20)
    print(result)
func()

二、名称空间

2.1 三种名称空间

built-in:内置名称空间,系统级,一个,随解释器执行而产生,解释器停止而销毁
global:全局名称空间,文件级,多个,随所属文件加载而产生,文件运行完毕而销毁
local:局部名称空间,函数级,多个,随所属函数执行而产生,函数执行完毕而销毁
    
注:加载顺序:built-in > global > local
num = 10
def fn2():
    num = 20
    print("this is fn2", num)

def fn3():
    num = 30
    print("this is fn3", num)


fn2()  # this is fn2 20
fn3()  # this is fn3 30
print(num)  # 10

三、作用域

3.1 LEGB

不同作用域之间名字不冲突,以达到名字的重用
直接查找顺序: Local -> Enclosing -> global -> built-in

built-in : 内置作用域,所有函数
global:全局作用域,当前文件所有函数
enclosing:嵌套作用域,当前函数
local:局部作用域
len = 10
def outer():
    len = 20
    def inner():
        len = 30
        print('1', len)
    inner()
    print('2', len)

outer()
print('3', len)  # 10 global --> built-in
del len
print('4', len)  # len地址--》built-in
nonlocal:
    作用:将L与E的名字统一(E中的名字需要提前定义)
    应用场景:如果想在被嵌套的函数中修改外部函数变量(名字)的值

案例:

def outer():
    num = 10
    print(num)  # 10

    def inner():
        nonlocal num
        num = 20
        print(num)  # 20
    inner()
    print(num)  # 20

outer()
# 第三行打印的 num ,是上一行定义的num
# 由于第五行用 nonlocal 声明了,所以inner里面的num和outer里面定义的num指代的是同一个东西

# 第七行打印的 num ,在第六行重新赋值(num指向的内存地址发生改变)

四、函数嵌套

将函数直接定义到另一个函数内部就可以使用外部函数中的名字
def outer():
    num = 20

    def inner():
        print(num)  # inner 可以直接使用outer中的名字
    inner()

outer()

五、闭包

函数嵌套(格式稍作改良)
inner : 可以使用outer的局部变量
def outer():
    num = 10
    def inner:
        print(num)
    return inner
fn = outer()
fn()
# 案例一:外部函数可以为闭包传递参数 
import time
def download():
    print('开始下载...')
    time.sleep(2)
    print('完成下载...')
    data = ""
    outer(data)
# 传参
def outer(data):
    def inner():
        # 保存,播放,删除等操作
        print("闭包打印:", data)
    inner()
download()
# 案例二:延迟执行
import requests
def outer(url):
    def get_html():
        html = requests.get(url)
        print(html.text)
    return get_html
# 先预定义多个爬虫方法,爬页面操作并未执行
baidu = outer('https://www.baidu.com')
python = outer('https://www.python.org')
sina = outer('https://www.sina.com.cn')
# 什么时候想爬什么页面就调用指定页面的爬虫方法
baidu()
sina()

2019.04.02 更新

四、装饰器

4.1 什么是装饰器

名字上来看,就是用来装饰的东西
从使用上来看,用来‘装饰’其他函数的函数,可以在原先函数功能基础上添加新的功能

4.2 开放封闭原则

在不改变调用方式和源码上增加功能
1.不能修改被装饰对象(函数)的源代码(封闭)
2.不能更改被修饰对象(函数)的调用方式,且能达到增加功能的效果(开放)

4.3 装饰器语法格式

def outer(fn):
    def inner(*args,**kwargs):
        #实现的功能
        result = fn(*args,**kwargs)
        #实现的功能
        return result
    return inner

4.4 案例们

4.4.1 实现原理
def fn():
    print("这是最初的函数!")
# 装饰器
def outer(func):
    def inner():
        func()
        print("打印一行给你看看,这就是添加的新功能!")
    return inner
fn = outer(fn)
fn()

# 打印结果:
# 这是最初的函数!
# 打印一行给你看看,这就是添加的新功能!

分析:
函数的执行顺序:
 1.python 程序是从上往下执行的,程序先读第1,4行程序(不执行),
 2.执行第9行代码,但是先从右侧开始执行,
 3.执行outer(fn),程序跳转到第4行,读入inner函数,跳转到8行,执行return,将inner地址返回
 4.返回的inner地址被fn接收,执行最后一行代码
 5.执行的fn()相当于执行inner()函数,也就是执行inner函数的函数体,由func()和其下一行组成
 6.其中第6行就是原先的函数 fn(),下一行就是增加的功能
 7.整个程序就实现了在原有基础函数不改变的情况下,增加了新的功能


# 外层函数
def outer(f):
    def inner():
        f()
        print("这里是新增加的功能!11")
    return inner
def wrap(f):
    def inner():
        f()
        print("这里也是新增加的功能!22")
    return inner
@wrap
@outer
def fn():
    print("这里是本来的功能!")
    
fn()

# 执行结果:
# 这里是本来的功能!
# 这里是新增加的功能!11
# 这里也是新增加的功能!22

分析:
多个装饰器同时装饰同一个函数,可以理解成 俄罗斯套娃 的形式,
谁离被装饰的函数越近,谁就先执行装饰任务

"""
# 有参数有返回值的函数被装饰
"""
def check_user(fn):
    def inner(usr, pwd):  # inner == login
        # 在原功能的基础上添加新功能
        # 如果名字长度超过 3 并且  名字是纯字母数字
        if not (len(usr) >= 3 and usr.isalpha()):
            print("账号验证失败!")
            return False

        # 满足条件情况下,执行原有的功能
        result = fn(usr, pwd)

        return result
    return inner

@check_user   # login = check_user(login)
def login(usr,pwd): 
    if usr == 'abc' and pwd =='123qwe':
        print('登录成功!')
        return True
    print('登录失败!')
    return False
    
login('abc', '123qwe')  # 应该先看最下面这一条执行的函数

总结:
    1.login有参数,所以inner与fn都有相同的参数
    2.login有返回值,所以inner与fn都有返回值

"""
# 装饰器最终写法
"""
def wrap(fn):
    def inner(*args, **kwargs):
        print('前增功能')
        result = fn(*args, **kwargs)
        print('后增功能')
        return result
    return inner

@wrap
def fn1():
    print('fn1的原有功能')
@wrap
def fn2(a, b):
    print('fn2的原有功能')
@wrap   
def fn3():
    print('fn3的原有功能')
    return True
@wrap
def fn4(a, *, x):
    print('fn4的原有功能')
    return True

fn1()
fn2(10, 20)
fn3()
fn4(10, x=20)

"""
# 带参装饰器
"""
def outer(input_color):
    def wrap(fn):
        if input_color == 'red':
            info = '\033[36;42m new action \033[0m'
        else:
            info = 'yellow:new action'
        def inner(*args, **kwargs):
            pass
            result = fn(*args, **kwargs)
            print(info)
            return result
        return inner
    return wrap  # outer(color) => wrap

color = input('color: ')

@outer(color)  # @outer(color) ==> @wrap  # func => inner
def func():
    print('func run')
func()


"""
# 案例
# 登录认证功能
"""
is_login = False

def login():
    usr = input('usr: ')
    if not (len(usr) >= 3 and usr.isalpha()):
        print('账号验证失败!')
        return False
    pwd = input('pwd: ')
    if usr == 'abc' and pwd == '123qwe':
        print('登录成功!')
        is_login = True
    else:
        print('登录失败!')
        is_login = False

# 完成一个登录状态验证的装饰器
def check_login(fn):
    def inner(*args,**kwargs):
        if is_login != True:
            print("你未登录!")
            login()
        # 执行被装饰的函数功能
        result = fn(*args,**kwargs)
        return result
    return inner

# 查看个人主页功能
@check_login
def home():
    print('个人主页')

# 销售功能
@check_login
def sell():
    print('清仓大甩卖!')

# 测试函数
home()
posted @ 2019-04-02 08:03  xt12321  阅读(587)  评论(0编辑  收藏  举报