python函数对象、函数嵌套、作用域、装饰器、高阶函数

一、函数对象

  1、函数是第一类对象,即函数可以当作数据传递

#1 可以被引用
#2 可以当作参数传递
#3 返回值可以是函数
#3 可以当作容器类型的元素

  2、利用该特性,优雅的取代多分支的if

def test():
    print('test')

def tes1():
    print('test1')

d={'test':test,'test1':tes1}

while True:
    s=input('-->').strip()
    if s in d:
        d[s]()

 

二、函数嵌套

  1、函数的嵌套调用

def test(x,y):
     if x>y:
         return x
     else:
         return y
def test1(a,b,c,d):
    r1=test(a,b)
    r2=test(r1,c)
    r3=test(r2,d)
    return r3
print(test1(1,2,3,4))

运行结果:
4

  2、函数的嵌套定义

def f1():
    def f2():
        def f3():
            print('from f3')
        f3()
    f2()

f1()
f3() #报错,为何?请看下一小节

 

 

三、重点 作用域

  1、Python中的作用域分4种情况 :

    L:local,局部作用域,即函数中定义的变量

    E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的
    G:global,全局变量,就是模块级别定义的变量
    B: built - in,系统固定模块里面的变量,比如int,bytearray等

x = int(2.9)  # int built-in
g_count = 0  # global


def outer():
    o_count = 1  # enclosing
    i_count = 8

    def inner():
        # i_count=2 #local
        print(i_count)

    inner()


outer()
#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
x=1
def f1():
    def f2():
        print(x)
    return f2
x=100
def f3(func):
    x=2
    func()
x=10000
f3(f1())

  3、global与nonlocal关键字 

#1.global关键字

count=10
def outer():
    global count
    count=5
    print(count)
outer()

#nonlocal关键字
如果要在一个嵌套的函数中修改嵌套作用域中的变量,则须使用nonlocal关键字。
def test():
    a=1
    def test1():
        nonlocal a
        a=2
        print('test1:',a)
    test1()
    print('test:',a)
test()

 

小结:
1、变量查找顺序:legb,作用域局部>外层作用域>当前模块中的全局>python内置作用域
2、只有模块、类、及函数才能引入新作用域
3、对于一个变量,内部作用域先声明就会覆盖外部变量,不声明直接使用,就会
使用外部作用域的变量
4、内部作用域修改外部作用域时,全局变量要使用global关键字,嵌套作用域
变量要使用nonlocal关键字。nonlocal是Python3新增的关键字,有了此关键字,就能
完美的实现闭包了

四、闭包函数

  1、什么是闭包

内部函数包含对外部作用域而非全局作用域的引用

提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,包起来
        def counter():
            n=0
            def incr():
                nonlocal n
                x=n
                n+=1
                return x
            return incr

        c=counter()
        print(c())
        print(c())
        print(c())
        print(c.__closure__[0].cell_contents) #查看闭包的元素

  2、闭包的意义:

#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
#应用领域:延迟计算(原来我们是传参,现在我们是包起来)
    from urllib.request import urlopen

    def index(url):
        def get():
            return urlopen(url).read()
        return get

    baidu=index('http://www.baidu.com')
    print(baidu().decode('utf-8'))


 

五、重要:装饰器

  装饰器就是闭包函数的一种应用场景

本质就是函数,功能是为其他函数添加附加功能
原则:
1、不修改被修饰函数的源代码
2、不修改被修饰函数的调用方式

装饰器的只是储备
装饰器=高阶函数+函数嵌套+闭包

无参数装饰器
import time

def timmer(func):
    def wapper(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        stop_time = time.time()
        print('函数运行时间是%s' % (stop_time - start_time))
        return res

    return wapper


@timmer
def cal(l):
    res = 0
    for i in l:
        res += i
    return res


print(cal(range(100)))


有参数装饰器
def t1(d='file'):
    def t2(func):
        def test(*args,**kwargs):
            name=input('name:')
            pwd=input('pwd:')

            if d == 'file':
                if name=='root' and pwd=='123':
                    print('成功')
                    res=func(*args,**kwargs)
                    return res
            elif d=='lala':
                print('lala')
        return test
    return t2

@t1(d='file')
def test3(name):
    print(name)

test3('root')

  3、装饰器补充:装饰器补充:wraps

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

@deco #相当于index=deco(index)
def index():
    '''哈哈哈哈'''
    print('from index')

print(index.__doc__)

  4、叠加多个装饰器

 1. 加载顺序(outter函数的调用顺序):自下而上
 2. 执行顺序(wrapper函数的执行顺序):自上而下
def outter1(func1): #func1=wrapper2的内存地址
    print('加载了outter1')
    def wrapper1(*args,**kwargs):
        print('执行了wrapper1')
        res1=func1(*args,**kwargs)
        return res1
    return wrapper1

def outter2(func2): #func2=wrapper3的内存地址
    print('加载了outter2')
    def wrapper2(*args,**kwargs):
        print('执行了wrapper2')
        res2=func2(*args,**kwargs)
        return res2
    return wrapper2

def outter3(func3): # func3=最原始的那个index的内存地址
    print('加载了outter3')
    def wrapper3(*args,**kwargs):
        print('执行了wrapper3')
        res3=func3(*args,**kwargs)
        return res3
    return wrapper3



@outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
def index():
    print('from index')

print('======================================================')
index()
View Code

  5、验证功能装饰器,模拟session

# 函数闭包模拟session,带参数装饰器


# 1、手写用户信息
user_list = [{'name': 'lala', 'passwd': '123'},
             {'name': 'wawa', 'passwd': '123'}]

#2、从文件中读取用户信息,再生成列表
# file='22.txt'
# with open(file,'r',encoding='utf-8') as f:
#     user_list=[]
#     for line in f.readlines():
#         li=line.strip('\n')
#         # li=line.split(',')
#         user_list.append(li) #读出文件信息后,重新生成用户信息,试过用eval,但是报错
#         print(user_list)


#最开始没有用户,登录状态是FALSE
current_dic={'username':None,'login':False}


#加认证参数,不同的函数使用不同的认证类型
def au_rz(rz_type='111'):
    def rzfunc(func):
        def test(*args, **kwargs):
            #最开始判断没有用户的时候,直接就执行函数
            global  current_dic

            print('各自的认证类型是:',rz_type)
            if rz_type=='111':

                if current_dic['username'] and current_dic['login']:
                    res=func(*args,**kwargs)
                    return res


                #输入用户名密码
                uname=input('用户名:').strip()
                pwd=input('密码:').strip()

                #再遍历current_dic的用户状态是否与user_list中一致
                for current_dic in user_list:
                    if uname==current_dic['name'] and pwd==current_dic['passwd']:
                        current_dic['username']=uname
                        current_dic['login']=True
                        res=func(*args,**kwargs)
                        return res

                else:
                    print('错误啦啦啦')
            elif rz_type=='222':
                res=func(*args,**kwargs)
                return res
            else:
                print('认证错误')


        return test
    return rzfunc


@au_rz(rz_type='111')
def index(name):
    print('%s欢迎进入主页' % name)


@au_rz(rz_type='22')
def shopping_cart(name,commodity):
    print('%s的购物车里有%s' % (name, commodity))

print('未登录前状态--》',current_dic)
index('主人')

print('登录后状态---》',current_dic)
shopping_cart('主人','衣服')
View Code

 

 

六、高阶函数

1、高阶函数定义:

  • 1、函数接收的参数是一个函数名
  • 2、函数的返回值是一个函数名
  • 3、满足上述条件任意一个,都可以成为高阶函数
    #1、高阶函数接收的参数是一个函数名
    import time
    def f1():
        time.sleep(1)
        print('lalala')
    
    
    def test(func):
        print(func)
        start_time = time.time()
        func()
        stop_time = time.time()
        print('函数运行时间是%s' % (stop_time - start_time))
    
    test(f1)
    
    #2、函数的返回值是一个函数名
    def fo1():
        print('来自foo1')
    
    
    def test(func):
        start_time = time.time()
        return func
        stop_time = time.time()
        print('函数运行时间是%s' % (stop_time - start_time))
    
    
    fo1 = test(fo1)
    fo1()

    2、装饰器的架子,装饰器=高阶函数+函数嵌套+闭包
    import time
    def timmer(func1):  # func1==test
        def wrapper():
            # print(func1)
            start_time = time.time()
            func1()  # 就是在运行test()
            stop_time = time.time()
            print('运行时间是%s' % (stop_time - start_time))
    
        return wrapper
    
    
    def test():
        time.sleep(1)
        print('test函数运行完毕')
    
    
    test = timmer(test)  # 返回的是wrapper地址
    test()  # 执行的是wrapper
    
     @timmer #就相当于test=timmer(test)
    
    # 函数闭包加上返回值、参数
    def timmer1(func2):
        def wapper(*args, **kwargs):
            start_time = time.time()
            res = func2(*args, **kwargs)  # 就是在运行test()
            stop_time = time.time()
            print('test1函数运行时间是%s' % (stop_time - start_time))
            return res
    
        return wapper
    
    
    @timmer1  # test1=timmer(test1)
    def test1(name, age, gender):
        time.sleep(1)
        print('test1函数运行完毕,name:[ %s],age:[%s],性别 [%s]' % (name, age, gender))
        return '这是test返回值'
    
    
    # test1=timmer1(test1)
    # test1()
    res = test1('zhuzhu', 20, '')
    print(res)
    
    ## 函数闭包补充解压序列
    # a,b,c=(1,2,3)#若是多一个参数或多一个值,报错,要一一对应
    View Code

     

     练习:

    六 练习题

    一:编写函数,(函数执行的时间是随机的)
    二:编写装饰器,为函数加上统计时间的功能
    三:编写装饰器,为函数加上认证的功能

    四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式

    五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录

    六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果

    七:为题目五编写装饰器,实现缓存网页内容的功能:
    具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中

    扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中

    八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作

    九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定
    注意:时间格式的获取
    import time
    time.strftime('%Y-%m-%d %X')

    # 一:编写函数,(函数执行的时间是随机的)
    import time, random,requests,os
    
    
    import time
    def random_time():
        #将时间设置成元组
        a=(1997,4,12,0,0,0,0,0,0)
        b=(2020,4,12,0,0,0,0,0,0)
    
        #生成时间戳
        start=time.mktime(a)
        end=time.mktime(b)
    
        t=random.randint(start,end)
    
        
        date=time.localtime(t) #时间戳生成元组
        date_str=time.strftime('%Y-%m-%d %X',date) #设置格式
    
        print('打印随机时间:',date_str)
        
    
    if __name__ =='__main__':
        random_time()
    
    
    #2、3
    import time
    def rz(rz_type='abc'):
        def timmer(func):
            def test(*args,**kwargs):
                print('认证类型是:',rz_type)
                if rz_type=='abc':
                    star=time.time()
                    res=func(*args,**kwargs)
                    stop=time.time()
    
                    print('函数运行时间%s'%(stop-star))
                    return res
                else:
                    print('认证失败')
    
            return test
        return timmer
    
    # @timmer #第2题
    @rz(rz_type='abc') #第三题,abc认证成功
    # @rz(rz_type='ab') #第三题,失败
    def cou(a,b):
        time.sleep(1)
        print('加法计算结果:%s'%(a+b))
    
    
    if __name__=='__main__':
        cou(1,2)
    
    
    #4、
    # 1、手写用户信息
    user_list = [{'name': 'lala', 'passwd': '123'},
                 {'name': 'wawa', 'passwd': '123'}]
    
    #2、从文件中读取用户信息,再生成列表
    # file='22.txt'
    # with open(file,'r',encoding='utf-8') as f:
    #     user_list=[]
    #     for line in f.readlines():
    #         li=line.strip('\n')
    #         # li=line.split(',')
    #         user_list.append(li) #读出文件信息后,重新生成用户信息,试过用eval,但是报错
    #         print(user_list)
    
    
    #最开始没有用户,登录状态是FALSE
    current_dic={'username':None,'login':False}
    
    
    #加认证参数,不同的函数使用不同的认证类型
    def au_rz(rz_type='111'):
        def rzfunc(func):
            def test(*args, **kwargs):
                #最开始判断没有用户的时候,直接就执行函数
                global  current_dic
    
                print('各自的认证类型是:',rz_type)
                if rz_type=='111':
    
                    if current_dic['username'] and current_dic['login']:
                        res=func(*args,**kwargs)
                        return res
    
    
                    #输入用户名密码
                    uname=input('用户名:').strip()
                    pwd=input('密码:').strip()
    
                    #再遍历current_dic的用户状态是否与user_list中一致
                    for current_dic in user_list:
                        if uname==current_dic['name'] and pwd==current_dic['passwd']:
                            current_dic['username']=uname
                            current_dic['login']=True
                            res=func(*args,**kwargs)
                            return res
    
                    else:
                        print('错误啦啦啦')
                elif rz_type=='222':
                    res=func(*args,**kwargs)
                    return res
                else:
                    print('认证错误')
    
    
            return test
        return rzfunc
    
    
    @au_rz(rz_type='111')
    def index(name):
        print('%s欢迎进入主页' % name)
    
    
    @au_rz(rz_type='22')
    def shopping_cart(name,commodity):
        print('%s的购物车里有%s' % (name, commodity))
    
    print('未登录前状态--》',current_dic)
    index('主人')
    
    print('登录后状态---》',current_dic)
    shopping_cart('主人','衣服')
    
    
    # 五:编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
    user = {'user': None, 'login_time': None, 'timeout': 0.000001}
    
    import random,time
    
    def rz(func):
        def test(*args, **kwargs):
    
            if user['user']:
                timeout = time.time() - user['login_time']
                if timeout < user['timeout']:
                    return func(*args, **kwargs)
    
            name = input('用户名:').strip()
            pwd = input('密码:').strip()
    
            if name == 'ww' and pwd == '123':
                user['user'] = name
                user['login_time'] = time.time()
                res = func(*args, **kwargs)
                return res
        return test
    
    
    
    @rz
    def index():
        time.sleep(random.randrange(3))
        print('welcom to index')
    
    @rz
    def home(name):
        time.sleep(random.randrange(3))
        print('%s、%s 主人welcom to home' % (name, name1))
    
    index()
    home('')
    
    
    
    # 六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
    import requests
    def page(url)
        return requests.get(url).text
    
    a=page('https://www.baidu.com/')
    print(a)
    
    
    
    #七:为题目五编写装饰器,实现缓存网页内容的功能:
    #具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
    
    #扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中
    import requests,os
    file='2.txt'
    
    def frw(func):
        def test(*args,**kwargs):
            if not os.path.exists(file):
                with open(file,'w'):pass
    
            if os.path.getsize(file):
                with open(file,'r',encoding='utf-8') as  f:
                    res=f.read()
            else:
                res=func(*args,**kwargs)
                with open(file,'w',encoding='utf-8') as f:
                    f.write(res)
            return res
        return test
    
    @frw
    def get_url(url):
        return requests.get(url).text
    
    res=get_url('https://www.baidu.com/')
    print(res)
    
    
    
    #7,扩展功能
    import requests,os,hashlib
    engine_settings={
        'file':{'dirname':'./db'},
        'mysql':{
            'host':'127.0.0.1',
            'port':3306,
            'user':'root',
            'password':'123'},
        'redis':{
            'host':'127.0.0.1',
            'port':6379,
            'user':'root',
            'password':'123'},
    }
    
    def make_cache(engine='file'):
        if engine not in engine_settings:
            raise TypeError('egine not valid')
        def deco(func):
            def wrapper(url):
                if engine == 'file':
                    m=hashlib.md5(url.encode('utf-8'))
                    cache_filename=m.hexdigest()
                    cache_filepath=r'%s/%s' %(engine_settings['file']['dirname'],cache_filename)
    
                    if os.path.exists(cache_filepath) and os.path.getsize(cache_filepath):
                        return open(cache_filepath,encoding='utf-8').read()
    
                    res=func(url)
                    with open(cache_filepath,'w',encoding='utf-8') as f:
                        f.write(res)
                    return res
                elif engine == 'mysql':
                    pass
                elif engine == 'redis':
                    pass
                else:
                    pass
    
            return wrapper
        return deco
    
    @make_cache(engine='file')
    def get(url):
        return requests.get(url).text
    
    # print(get('https://www.python.org'))
    print(get('https://www.baidu.com'))
    
    
    #8、八:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
    d={}
    def make_dic(name):
        def test1(func):
            d[name]=func
        return test1
    
    @make_dic('t1')
    def t1():
        print('t1')
    
    @make_dic('t2')
    def t2():
        print('t2')
    
    @make_dic('t3')
    def t3():
        print('t3')
    
    @make_dic('t4')
    def t4():
        print('t4')
    
    print(d)
    
    
    
    '''
    九 编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定
    注意:时间格式的获取
    import time
    time.strftime('%Y-%m-%d %X')
    '''
    import time
    def frw(logfiel):
        def test(func):
            if not os.path.exists(logfiel):
                with open(logfiel,'w'):pass
    
            def write(*args,**kwargs):
                res=func(*args,**kwargs)
                t=time.strftime('%Y-%m-%d %X',time.localtime())
    
                with open(logfiel,'a',encoding='utf-8') as f:
                    f.write('%s %s run\n'%(t,func.__name__))
                return res
            return write
        return test
    
    @frw(logfiel='22')
    def  f1():
        print('f1 run')
    
    f1()
    View Code

     

    #第4题,eval写法,但是老是报错,麻了
    db='22.txt'
    login_status={'user':None,'status':False}
    def auth(auth_type='file'):
        def auth2(func):
            def wrapper(*args,**kwargs):
                if login_status['user'] and login_status['status']:
                    return func(*args,**kwargs)
                if auth_type == 'file':
                    with open(db,'r',encoding='utf-8') as f:
                        dic=eval(f.read())
                        
                    name=input('username: ').strip()
                    password=input('password: ').strip()
                    if name in dic and password == dic[name]:
                        login_status['user']=name
                        login_status['status']=True
                        res=func(*args,**kwargs)
                        return res
                    else:
                        print('username or password error')
                elif auth_type == 'sql':
                    pass
                else:
                    pass
            return wrapper
        return auth2
    
    @auth()
    def index():
        print('index')
    
    @auth(auth_type='file')
    def home(name):
        print('welcome %s to home' %name)
    
    index()
    home('lala')
    第4题eval写法

     

posted @ 2022-08-03 21:54  ilspring  阅读(161)  评论(0)    收藏  举报