zoukankan      html  css  js  c++  java
  • 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 = '33[36;42m new action 33[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()
    
  • 相关阅读:
    JFinal Web开发学习(二)目录、架构、package设计
    JFinal Web开发学习(一)开启HelloWorld
    使用JFinal实现使用MVC获取表单中的数据并将提示信息返回给另一jsp页面。
    [JSOI2010]满汉全席 -- 2-SAT
    [HNOI/AHOI2018] 道路
    ZJOI2006 物流运输
    HNOI2005 狡猾的商人
    打上标记(给树)
    hdu-6201
    Wannfly day2 采蘑菇
  • 原文地址:https://www.cnblogs.com/xt12321/p/10640323.html
Copyright © 2011-2022 走看看